結果

問題 No.2398 ヒドラ崩し
ユーザー qwewe
提出日時 2025-05-14 13:09:19
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
TLE  
実行時間 -
コード長 11,142 bytes
コンパイル時間 1,309 ms
コンパイル使用メモリ 122,796 KB
実行使用メモリ 51,968 KB
最終ジャッジ日時 2025-05-14 13:10:30
合計ジャッジ時間 5,974 ms
ジャッジサーバーID
(参考情報)
judge5 / judge1
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 13 TLE * 1 -- * 17
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <functional> // Required for std::function
#include <vector>

using namespace std;

// Memoization map for computed Grundy values. Key is the Hydra string state.
map<string, int> memo;
// Memoization map for computed state strings h[n]. Key is pair {Hydra string state, n}.
map<pair<string, int>, string> state_cache; 

// Heuristic limits to prevent excessive computation or memory usage.
// MAX_LEN: Maximum allowed string length for a Hydra state. States exceeding this are considered invalid/unreachable.
// This helps manage memory usage. 512MB memory limit suggests roughly 5*10^8 characters total storage.
// Initial string length up to 10^5. Strings can grow. MAX_LEN=200000 is a heuristic guess.
const int MAX_LEN = 200000; 
// MAX_K: Limit for exploring states h[k] for k=1, 2, ..., MAX_K.
// A common heuristic in competitive programming for games with infinite moves.
const int MAX_K = 61; 
// MAX_DEPTH: Limit for recursion depth to prevent stack overflow.
const int MAX_DEPTH = 1000; 
// Current recursion depth counters
int current_depth_grundy = 0; // Depth counter for calculate_grundy function
int current_depth_state = 0; // Depth counter for get_state function

// Utility function to check if a string ends with "()"
bool ends_with_empty_paren(const string& s) {
    // Check if string length is at least 2 and last two characters are "()"
    return s.length() >= 2 && s.substr(s.length() - 2) == "()";
}

// Forward declarations for recursive functions
string get_state(string h, int n); 
int calculate_grundy(string h); 

/**
 * @brief Computes the Hydra state string h[n] according to the problem rules.
 * 
 * @param h The current Hydra string state.
 * @param n The positive integer parameter for the move.
 * @return string The resulting Hydra string state h[n]. Returns empty string if computation fails due to limits or invalid input.
 */
string get_state(string h, int n) {
    // Base cases: empty hydra or invalid n
    if (h.empty()) return "";
    if (n <= 0) return ""; 

    // Check cache for previously computed state h[n]
    pair<string, int> cache_key = {h, n};
    if (state_cache.count(cache_key)) {
        return state_cache[cache_key];
    }

    // Check recursion depth limit for state generation
    if (current_depth_state > MAX_DEPTH) return ""; // Return empty string indicating failure/limit

    current_depth_state++; // Increment depth counter
    string result = ""; // Stores the computed state string

    // Apply the rules based on the structure of h:

    // Case 1: h = h'()
    // If h ends with "()", then h[n] = h'. The value of n doesn't matter.
    if (ends_with_empty_paren(h)) {
        result = h.substr(0, h.length() - 2);
    } else {
        // Case 2/3: h does not end with "()". It must be of the form h = h0 (h1).
        string h0, h1;
        int component_start = -1; // Start index of the last prime component (h1)
        int balance = 0; // Parenthesis balance counter
        
        // Scan from right to find the start of the last prime component
        for (int k = h.length() - 1; k >= 0; --k) {
            if (h[k] == ')') balance++;
            else if (h[k] == '(') balance--;
            if (balance == 0) { // Found the boundary where balance returns to 0
                component_start = k;
                break;
            }
        }
        
        // Validate the structure found
        if (component_start == -1) { // If balance never returns to 0 except at the beginning
             // This means the entire string is wrapped like (....)
             if (h[0] == '(' && h.back() == ')') { component_start = 0; } // Valid case: h=(...)
             else { current_depth_state--; return ""; } // Error: Invalid structure
        } else if(h[component_start] != '(') { // Component boundary must start with '(' for form (h1)
             current_depth_state--; return ""; // Error: Invalid structure found
        }

        // Extract h0 and h1 from h = h0 + "(" + h1 + ")"
        h0 = h.substr(0, component_start);
        h1 = h.substr(component_start + 1, h.length() - component_start - 2);

        // h1 must be a non-empty ("living") Hydra for Case 2/3 logic.
        // If h1 is empty, it means the last component was "()", handled by Case 1.
        if (h1.empty()) { current_depth_state--; return ""; } // Error state, should have been Case 1

        // Subcases based on whether h1 ends with "()"
        
        // Case 2a: h1 = h1'()
        if (ends_with_empty_paren(h1)) { 
            string h1_prime = h1.substr(0, h1.length() - 2); // Get h1' by removing trailing "()"
            string h1_prime_paren = "(" + h1_prime + ")"; // Construct string "(h1')"
            
            if (n == 1) { // If n=1, h[1] = h0 (h1')
                result = h0 + h1_prime_paren;
            } else { // If n > 1, h[n] = h[n-1] (h1')
                // Recursively compute h[n-1] first
                string prev_state = get_state(h, n - 1); 
                // Check if the recursive call succeeded
                if (prev_state == "") { 
                   current_depth_state--; return ""; // Propagate failure
                }
                // Concatenate h[n-1] and (h1')
                result = prev_state + h1_prime_paren;
            }
        } else { // Case 2b: h1 does not end with "()"
            // h[n] = h0 (h1[n])
            // Recursively compute h1[n] first
             string h1_n_state = get_state(h1, n); 
             // Check if the recursive call succeeded
             if (h1_n_state == "") { 
                 current_depth_state--; return ""; // Propagate failure
             }
             // Concatenate h0 and (h1[n])
             result = h0 + "(" + h1_n_state + ")";
        }
    }
    
    // Check final computed state length against limit
    if (result.length() > MAX_LEN) {
         current_depth_state--; return ""; // Return empty string if state is too large
    }
    
    current_depth_state--; // Decrement depth counter before returning

    // Cache the computed state if it's valid (not empty string representing error/limit).
    // Allow caching empty string result if initial h was empty.
    if (!result.empty() || h.empty()) { 
         state_cache[cache_key] = result;
    }
    return result;
}

/**
 * @brief Computes the Grundy value (nim-value) for a given Hydra state string h.
 * Uses recursion with memoization (dynamic programming).
 * 
 * @param h The Hydra string state.
 * @return int The Grundy value G(h). Returns 0 if computation fails due to limits.
 */
int calculate_grundy(string h) {
    // Base case: empty Hydra is a terminal state with Grundy value 0.
    if (h.empty()) {
        return 0;
    }
    // Check memoization table for cached Grundy value
    if (memo.count(h)) {
        return memo[h];
    }
    
    // Check recursion depth limit for Grundy calculation
    if (current_depth_grundy > MAX_DEPTH) return 0; // Return 0 (losing state) as fallback

    current_depth_grundy++; // Increment depth counter

    // Set to store Grundy values of reachable states
    set<int> next_grundy_values;

    // Determine reachable states based on the structure of h:

    // Case 1: h = h'()
    if (ends_with_empty_paren(h)) { 
        string h_prime = h.substr(0, h.length() - 2); // The only reachable state is h'
        // Check string length before recursive call to avoid exceeding limits
        if (h_prime.length() <= MAX_LEN) {
            next_grundy_values.insert(calculate_grundy(h_prime));
        }
    } else { // Case 2/3: h = h0 (h1)
        // Re-parse h to find h0 and h1. This could be optimized if parse results were passed.
        string h0, h1;
        int component_start = -1;
        int balance = 0;
         for (int k = h.length() - 1; k >= 0; --k) {
             if (h[k] == ')') balance++;
             else if (h[k] == '(') balance--;
             if (balance == 0) {
                 component_start = k;
                 break;
             }
         }

        // Validate structure
        if (component_start == -1) {
            if (h[0] == '(' && h.back() == ')') { component_start = 0; }
            else { current_depth_grundy--; return 0; } // Error/placeholder G
        } else if(h[component_start] != '(') {
            current_depth_grundy--; return 0; // Error/placeholder G
        }

        h0 = h.substr(0, component_start);
        h1 = h.substr(component_start + 1, h.length() - component_start - 2);

        if (h1.empty()) { current_depth_grundy--; return 0; } // Error/placeholder G

        // Explore reachable states by trying k = 1, 2, ..., MAX_K
        // The logic applies similarly to Case 2a and 2b, based on how get_state(h, k) behaves.
        for (int k = 1; k <= MAX_K; ++k) {
            // Compute the state resulting from move k
            string next_state = get_state(h, k); 
            
            // Check if state generation failed (due to limits/errors)
            // The condition `!h.empty() && k > 0` ensures we distinguish actual empty state target from error indicator ""
            if (next_state == "" && !h.empty() && k > 0) { 
                // Stop exploring if state generation fails
                break; 
            }
            
            // Recursively compute Grundy value of the reachable state
            int gk = calculate_grundy(next_state);
            next_grundy_values.insert(gk); // Add to set of reachable Grundy values

            // Heuristic optimization: stop exploring k if the mex value seems stable.
            // Compute current mex based on values found so far.
            int current_mex = 0;
            while (next_grundy_values.count(current_mex)) current_mex++;
            // If we've explored k up to current_mex + 2 (and at least 5 steps), assume mex won't change.
            if (k >= current_mex + 2 && k >= 5) break; 
        }
    }

    // Calculate the mex (Minimum Excluded value) of the set of reachable Grundy values.
    // This is the Grundy value G(h).
    int mex = 0;
    while (next_grundy_values.count(mex)) {
        mex++;
    }
    
    current_depth_grundy--; // Decrement depth counter before returning
    
    // Store computed Grundy value in memoization table
    return memo[h] = mex;
}


int main() {
    // Use faster I/O operations
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    string H; // Input Hydra string
    cin >> H;

    // Reset recursion depth counters for the main calculation
    current_depth_grundy = 0; 
    current_depth_state = 0; 
    
    // Calculate the Grundy value of the initial state H
    int result = calculate_grundy(H);

    // Determine the winner based on the Grundy value G(H)
    // If G(H) > 0, the first player has a winning strategy.
    // If G(H) = 0, the second player has a winning strategy.
    if (result > 0) {
        cout << 0 << endl; // First player (Hydra Lover Robot) wins
    } else {
        cout << 1 << endl; // Second player (Hydra Lover Robot Lover Robot) wins
    }

    return 0;
}
0