結果

問題 No.2398 ヒドラ崩し
ユーザー qwewe
提出日時 2025-05-14 12:57:13
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
WA  
実行時間 -
コード長 13,039 bytes
コンパイル時間 1,710 ms
コンパイル使用メモリ 112,396 KB
実行使用メモリ 688,296 KB
最終ジャッジ日時 2025-05-14 12:59:06
合計ジャッジ時間 5,385 ms
ジャッジサーバーID
(参考情報)
judge1 / judge3
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 15 WA * 9 MLE * 1 -- * 6
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <functional>
#include <stdexcept> // For exception handling
#include <string_view> // Used for efficient substring operations without copying

using namespace std;

// Map for memoization: stores computed Grundy values for Hydra strings.
// Using std::string as key because std::string_view requires careful lifetime management
// which is tricky with recursive calls potentially creating temporary strings.
map<string, int> grundy_values;

// Forward declaration of the main recursive function
int calculate_grundy(const string& h); 

/**
 * @brief Parses a Hydra string `h` to identify its structure based on the problem definition.
 * Finds the decomposition into `prefix` and `last_component`.
 * The `last_component` is the rightmost primitive Hydra component, which is either "()"
 * or of the form "(k)" where k is a Hydra.
 *
 * @param h The Hydra string to parse.
 * @param prefix Output parameter for the part of the string before the last component.
 * @param last_component Output parameter for the last primitive component string.
 * @return true if parsing is successful, false otherwise.
 */
bool parse_hydra_last_component(const string& h, string& prefix, string& last_component) {
    if (h.empty()) return false; // Cannot parse empty string

    int balance = 0;
    int split_pos = -1; // Position of the '(' that starts the last component

    // Iterate from right to left to find the beginning of the last top-level component
    for (int i = h.length() - 1; i >= 0; --i) {
        if (h[i] == ')') {
            balance++;
        } else { // h[i] == '('
            balance--;
        }
        // When balance becomes 0, we have found a potential component boundary.
        // If this boundary starts with '(', it marks the beginning of the last primitive component.
        if (balance == 0 && h[i] == '(') {
            split_pos = i;
            break;
        }
    }
    
    // Case 1: Found a component like `(...)` covering the suffix
    if (split_pos != -1) { 
        prefix = h.substr(0, split_pos);
        last_component = h.substr(split_pos);
        // Basic validation: check if it conforms to `(...)` structure. Length must be at least 2.
        if (last_component.length() < 2 || last_component.front() != '(' || last_component.back() != ')') {
             // This indicates potential parsing error or invalid Hydra structure.
             return false; 
        }
        return true;
    }

    // Case 2: The string ends with `()` but not as part of a larger `(...)` suffix component. E.g., `(())()`
    // This check covers cases where the last primitive component is simply `()`.
    if (h.length() >= 2 && h.substr(h.length() - 2) == "()") {
         prefix = h.substr(0, h.length() - 2);
         last_component = "()";
         return true;
    }
    
    // Case 3: The entire string `h` is a single primitive hydra like `()` or `(())`.
    // Check if `h` starts with '(' and ends with ')', and the content inside is a valid balanced Hydra structure.
    if (h.length() >= 2 && h.front() == '(' && h.back() == ')') {
        int check_balance = 0;
        bool balanced_internal = true;
        // Validate the balance of parentheses strictly inside the outer pair
        for(size_t i = 1; i < h.length() - 1; ++i) {
             if (h[i] == '(') check_balance++;
             else check_balance--;
             // If balance drops below 0, it means ')' appeared before matching '('. Invalid structure.
             if (check_balance < 0) { balanced_internal = false; break; } 
        }
        // Final balance must be 0 for the internal part to be a valid Hydra (or empty string).
        if (balanced_internal && check_balance == 0) { 
            prefix = ""; // No prefix, the whole string is the last component.
            last_component = h;
            return true;
        }
    }

    // If none of the known structures match, parsing fails.
    return false; 
}

/**
 * @brief Computes the state h[1] for a given Hydra h.
 * This function is used as a helper, particularly for Subcase 2.2 logic.
 * Throws runtime_error on parsing failure or unexpected structure.
 * 
 * @param h The Hydra string.
 * @return The resulting Hydra string after applying the [1] operation.
 */
string compute_h_1(const string& h) {
     string prefix, last_component;
     // Attempt to parse the Hydra structure.
     if (!parse_hydra_last_component(h, prefix, last_component)) {
        // If standard parsing fails, re-attempt specific edge cases like h = "()"
        // This logic could be integrated better into parse_hydra_last_component.
        if (h == "()") {
             prefix = ""; last_component = "()";
        } else {
            throw runtime_error("compute_h_1 parse failed: " + h);
        }
     }

     // Case 1 based on definition: h = h'() => h[1] = h'
     if (last_component == "()") { 
         return prefix;
     } 
     // Case 2 based on definition: h = h0 + (h1)
     else { 
         string h1 = last_component.substr(1, last_component.length() - 2);
         // h1 must be non-empty because last_component is not "()"
         if (h1.empty()) throw runtime_error("compute_h_1: h1 empty for " + h); // Should not happen

         // Check if h1 fits Subcase 2.1: h1 = h1'()
         bool h1_ends_in_empty_parens = (h1.length() >= 2 && h1.substr(h1.length() - 2) == "()");
         
         if (h1_ends_in_empty_parens) { // Subcase 2.1 applies to h: h[1] = prefix + (h1')
             string h1_prime = h1.substr(0, h1.length() - 2);
             return prefix + "(" + h1_prime + ")"; 
         } else { // Subcase 2.2 applies to h: h[1] = prefix + (h1[1])
             // Recursively compute h1[1]
             string h1_1 = compute_h_1(h1); 
             return prefix + "(" + h1_1 + ")"; 
         }
     }
}

// Define maximum string length and map size limits to prevent resource exhaustion.
const size_t MAX_STRING_LENGTH = 400000; // Heuristic based on typical contest limits + buffer
const size_t MAX_MAP_SIZE = 500000; // Max states to memoize. Adjust based on memory limits.

/**
 * @brief Calculates the Grundy value (or Nim-sum) for a given Hydra state `h`.
 * Uses memoization to store results and avoid recomputation.
 * Implements the recursive definition of the game moves h -> h[n].
 *
 * @param h The Hydra string representing the current game state.
 * @return The Grundy value of state h.
 */
int calculate_grundy(const string& h) {
    // Base case: The empty string (dead Hydra) is a terminal state with Grundy value 0.
    if (h.empty()) {
        return 0;
    }
    // Check memoization table first.
    auto it = grundy_values.find(h);
    if (it != grundy_values.end()) {
        return it->second;
    }

    // Limit state space explored to prevent excessive resource usage.
    if (grundy_values.size() > MAX_MAP_SIZE) { 
       throw runtime_error("State space limit exceeded");
    }

    // Set to store Grundy values of reachable next states.
    set<int> next_grundy_values;
    string prefix, last_component;

    // Parse the Hydra string to determine its structure for applying game rules.
    if (!parse_hydra_last_component(h, prefix, last_component)) {
         throw runtime_error("Failed to parse hydra: " + h);
    }

    // Apply game rules based on the Hydra structure:
    
    // Case 1: h = prefix + "()". This corresponds to `h = h'()` in problem statement.
    if (last_component == "()") {
         // For any n >= 1, h[n] = prefix. There's only one next state.
         next_grundy_values.insert(calculate_grundy(prefix));
    } 
    // Case 2: h = prefix + "(h1)" where h1 is a non-empty Hydra. Corresponds to `h = h0 (h1)`.
    else {
        // Extract h1 from "(h1)" structure.
        string h1 = last_component.substr(1, last_component.length() - 2);
        // Ensure h1 is not empty. This should hold if last_component is not "()".
        if (h1.empty()) throw runtime_error("Case 2 error: h1 is empty for " + h);

        // Check if h1 matches Subcase 2.1 structure: h1 = h1'()
        bool h1_ends_in_empty_parens = (h1.length() >= 2 && h1.substr(h1.length() - 2) == "()");
        
        if (h1_ends_in_empty_parens) {
            // Subcase 2.1: h1 = h1'()
            string h1_prime = h1.substr(0, h1.length() - 2);
            // Construct the component (h1') used in state transitions.
            string P_h1_prime = "(" + h1_prime + ")";

            // State for n=1: h[1] = prefix + (h1')
            string h_1 = prefix + P_h1_prime;
            // Check length before recursive call.
            if (h_1.length() > MAX_STRING_LENGTH) throw runtime_error("String length limit exceeded: h[1]"); 
            next_grundy_values.insert(calculate_grundy(h_1));

            // States for n > 1: h[n] = h[n-1] + (h1'). Generates potentially infinite sequence H'P, H'P^2, ...
            string current_h_n = h_1;
            // Heuristic loop limit: compute limited number of next states. Grundy values are often small.
            int max_k = 300; 
            for (int k = 2; k <= max_k; ++k) {
                 // Check string length before appending to prevent excessive growth.
                 if (current_h_n.length() + P_h1_prime.length() > MAX_STRING_LENGTH) break; 
                 current_h_n += P_h1_prime;
                
                next_grundy_values.insert(calculate_grundy(current_h_n));

                // Optimization heuristic: stop early if enough small Grundy values are found
                // to determine the mex, assuming mex won't be excessively large.
                int current_mex = 0;
                while(next_grundy_values.count(current_mex)) current_mex++;
                 if (k > current_mex + 5) break; 
            }
        } else {
            // Subcase 2.2: h1 is live and does not end in "()".
            // Rule: h[n] = prefix + "(" + h1[n] + ")"
            // This rule requires computing h1[n]. This is the most complex case.
            // Full implementation requires a function `compute_h_k(h, k)`.
            // Simplified approach: Compute only for k=1 as a heuristic. Assumes this is enough to determine the winner.
             string h1_1;
             try {
                // Compute h1[1] using the helper function.
                h1_1 = compute_h_1(h1);
             } catch (const runtime_error& e) {
                 // Propagate errors from compute_h_1
                 throw runtime_error("Error computing h1[1] for h1=" + h1 + ": " + e.what());
             } catch (...) {
                 // Catch any other exceptions during h1[1] computation.
                 throw runtime_error("Unknown error computing h1[1] for h1=" + h1);
             }

             // Construct the state h[1] = prefix + (h1[1])
             string h_1_state = prefix + "(" + h1_1 + ")";
             // Check length constraint.
             if (h_1_state.length() > MAX_STRING_LENGTH) throw runtime_error("String length limit exceeded: h[1] in case 2.2");
             // Compute Grundy value for this state.
             next_grundy_values.insert(calculate_grundy(h_1_state));
             
             // Note: This implementation only considers h[1] for Subcase 2.2.
             // This is a major simplification and might be incorrect if moves with n > 1 are critical.
             // A full solution would need to handle h[n] for n > 1 in this case.
        }
    }

    // Compute the Mex (Minimum Excluded value) of the set of next state Grundy values.
    int mex = 0;
    while (next_grundy_values.count(mex)) {
        mex++;
    }
    // Store the computed Grundy value in the memoization table and return it.
    return grundy_values[h] = mex;
}


int main() {
    // Optimize C++ standard streams
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    string H; // Input Hydra string
    cin >> H;
    
    try {
       // Calculate the Grundy value of the initial state H.
       int result_grundy = calculate_grundy(H);
       
       // Determine the winner based on the Grundy value.
       // G > 0 means the first player has a winning strategy (N-position).
       // G = 0 means the second player has a winning strategy (P-position).
       if (result_grundy > 0) {
           cout << 0 << endl; // First player (ヒドラ大好きbot) wins
       } else {
           cout << 1 << endl; // Second player (ヒドラ大好きbot大好きbot) wins
       }
    } catch (const exception& e) {
         // Handle potential runtime errors (e.g., state space limit, parsing issues).
         cerr << "Exception: " << e.what() << endl;
         // Exit with error status. The problem implies a definite winner exists.
         // Failure means our implementation hit limits or has bugs.
         return 1; 
    } catch (...) {
        // Catch any other unexpected exceptions.
        cerr << "Unknown exception occurred." << endl;
        return 1; 
    }
    
    return 0; // Successful execution
}
0