結果
問題 |
No.726 Tree Game
|
ユーザー |
![]() |
提出日時 | 2025-05-14 12:55:48 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 3 ms / 2,000 ms |
コード長 | 7,800 bytes |
コンパイル時間 | 4,936 ms |
コンパイル使用メモリ | 86,084 KB |
実行使用メモリ | 6,272 KB |
最終ジャッジ日時 | 2025-05-14 12:57:21 |
合計ジャッジ時間 | 1,872 ms |
ジャッジサーバーID (参考情報) |
judge1 / judge4 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | AC * 25 |
ソースコード
#include <iostream> #include <vector> #include <map> #include <set> #include <utility> // For std::pair // Use __uint128_t for intermediate calculations in modular exponentiation to avoid overflow. // This type is available in GCC and Clang. #ifdef __GNUC__ using u128 = __uint128_t; #else // Provide a fallback for compilers that don't support __uint128_t. // Note: This fallback might lead to overflow for very large intermediate values in the power function. // Competitive programming environments usually support __uint128_t. using u128 = unsigned long long; #warning "__uint128_t not supported, potential overflow in modular exponentiation." #endif // Use unsigned long long for large integers potentially involved in primality testing. using u64 = unsigned long long; // Modular exponentiation function: computes (base^exp) % mod // Uses __uint128_t for intermediate products to prevent overflow. u64 power(u64 base, u64 exp, u64 mod) { u64 res = 1; base %= mod; while (exp > 0) { if (exp % 2 == 1) res = (u128)res * base % mod; base = (u128)base * base % mod; exp /= 2; } return res; } // Helper function for Miller-Rabin primality test. // Checks if 'n' is composite using 'a' as a base witness. // 'd' and 's' satisfy n-1 = d * 2^s where d is odd. bool check_composite(u64 n, u64 a, u64 d, int s) { // Calculate a^d % n u64 x = power(a, d, n); // If x is 1 or n-1, n might be prime (pass test for base a) if (x == 1 || x == n - 1) return false; // Repeat s-1 times: square x and check if it becomes n-1 for (int r = 1; r < s; r++) { x = (u128)x * x % n; if (x == n - 1) return false; // n might be prime } // If x never became n-1 after squaring, n is definitely composite return true; } // Miller-Rabin primality test function. // Uses deterministic set of bases {2, 7, 61} which is sufficient for n < 4,759,123,141. // This covers the problem constraints (Y, X <= 10^9) and likely coordinate ranges. bool is_prime(u64 n) { // Handle base cases: 0, 1 are not prime. 2, 3, 5 are prime. if (n < 2) return false; if (n == 2 || n == 3 || n == 5) return true; // Quick check for divisibility by small primes 2, 3, 5 if (n % 2 == 0 || n % 3 == 0 || n % 5 == 0) return false; // Pre-check small numbers < 49 using properties. Any number < 49 not divisible by 2,3,5 must be prime or 7*7=49. // The remaining primes < 49 are: 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47. if (n < 49) { // Note: 7*7 = 49. We only check up to n < 49. // Any remaining number must be one of these primes. return true; } // If n >= 49, proceed with Miller-Rabin test int s = 0; u64 d = n - 1; // Find d and s such that n-1 = d * 2^s where d is odd while ((d & 1) == 0) { d >>= 1; s++; } // Test with bases {2, 7, 61} for (u64 a : {2, 7, 61}) { // We need a < n. Since n >= 49, this holds for a=2, 7, 61. // Check if n is one of the bases itself (though redundant because we checked small primes already) if (n == a) return true; // Perform Miller-Rabin test with base 'a' if (check_composite(n, a, d, s)) return false; // Found a witness 'a' that proves n is composite } // If n passed Miller-Rabin test for all selected bases, it is very likely prime. // For the chosen bases and range, this is deterministic. return true; } // Memoization table to store computed Grundy values. // Key: pair of coordinates (y, x). Value: Grundy number G(y, x). // Use std::map for simplicity. For higher performance, std::unordered_map could be considered. std::map<std::pair<long long, long long>, int> memo; // Recursive function to compute the Grundy value (Nim-sum) G(y, x) for a state (y, x). // Assumes this function is called only for non-prime cells (y, x). int compute_grundy(long long y, long long x) { // State represented as a pair of coordinates std::pair<long long, long long> current_state = {y, x}; // Check if the Grundy value for this state is already computed and stored in the map auto it = memo.find(current_state); if (it != memo.end()) { return it->second; // Return memoized value } // Set to store the Grundy values of reachable next states (neighbors in the game graph) std::set<int> neighbor_grundy_values; // Explore the move upwards to (y+1, x) long long next_y1 = y + 1; long long next_x1 = x; // A move is valid only if the destination cell is non-prime. // A cell (i, j) is non-prime if both i and j are NOT prime. if (!is_prime(next_y1) && !is_prime(next_x1)) { // Recursively compute the Grundy value of the next state and add it to the set neighbor_grundy_values.insert(compute_grundy(next_y1, next_x1)); } // Explore the move rightwards to (y, x+1) long long next_y2 = y; long long next_x2 = x + 1; // Check if the destination cell is non-prime if (!is_prime(next_y2) && !is_prime(next_x2)) { // Recursively compute the Grundy value and add it to the set neighbor_grundy_values.insert(compute_grundy(next_y2, next_x2)); } // Compute the Minimum Excluded value (mex) of the set of neighbor Grundy values. // mex(S) is the smallest non-negative integer not present in the set S. int mex = 0; while (neighbor_grundy_values.count(mex)) { mex++; } // Store the computed mex value (Grundy value for current state) in the memoization table // and return it. return memo[current_state] = mex; } int main() { // Optimize C++ standard input/output streams for speed std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); long long Y, X; // Input coordinates for the starting position std::cin >> Y >> X; // Flag to determine if the first player has a winning strategy bool first_can_win = false; // --- Determine the winner based on the game rules --- // The first player wins if they can make a move to a state (cell) which is a P-position (losing position for the next player). // A P-position has a Grundy value of 0. // Evaluate the move upwards to (Y+1, X) long long next_y1 = Y + 1; long long next_x1 = X; // Check if the destination cell (Y+1, X) is a non-prime cell if (!is_prime(next_y1) && !is_prime(next_x1)) { // If it's non-prime, compute its Grundy value. If G(Y+1, X) == 0, this is a winning move. if (compute_grundy(next_y1, next_x1) == 0) { first_can_win = true; // Found a winning move } } // If (Y+1, X) is a prime cell, moving there is an immediate loss, so it's not a winning move. // Evaluate the move rightwards to (Y, X+1), only if a winning move hasn't been found yet. if (!first_can_win) { long long next_y2 = Y; long long next_x2 = X + 1; // Check if the destination cell (Y, X+1) is non-prime if (!is_prime(next_y2) && !is_prime(next_x2)) { // If it's non-prime, compute its Grundy value. If G(Y, X+1) == 0, this is a winning move. if (compute_grundy(next_y2, next_x2) == 0) { first_can_win = true; // Found a winning move } } // If (Y, X+1) is a prime cell, moving there is an immediate loss. } // Output the result based on whether the first player has a winning move if (first_can_win) { std::cout << "First" << std::endl; } else { // If no move leads to a Grundy 0 state, the first player loses (assuming optimal play). std::cout << "Second" << std::endl; } return 0; }