結果
| 問題 |
No.715 集合と二人ゲーム
|
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 12:56:31 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 60 ms / 2,000 ms |
| コード長 | 6,717 bytes |
| コンパイル時間 | 2,500 ms |
| コンパイル使用メモリ | 87,456 KB |
| 実行使用メモリ | 7,296 KB |
| 最終ジャッジ日時 | 2025-05-14 12:58:50 |
| 合計ジャッジ時間 | 5,032 ms |
|
ジャッジサーバーID (参考情報) |
judge1 / judge2 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 60 |
ソースコード
#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <set> // Used only during precomputation
// Global array to store precomputed Grundy values up to a certain limit
// The Grundy values are needed for lengths of consecutive blocks of numbers.
const int PRECOMPUTE_LIMIT = 88; // We need values up to index 87 to cover the first cycle and the pre-period part.
int g_values[PRECOMPUTE_LIMIT];
// Periodicity parameters for the Grundy sequence g(L)
// Based on analysis and external resources (like OEIS A002187 for related games), this sequence is periodic.
const int PERIOD = 34; // The period length
const int PERIOD_START_INDEX = 54; // The index from which the sequence becomes periodic
// Function to precompute Grundy values up to PRECOMPUTE_LIMIT
// This function uses dynamic programming based on the game rules.
// g(L) = mex { g(L') | L' is a state reachable from L in one move }
void precompute_g() {
// Base case: g(0) = 0 for an empty block/set
g_values[0] = 0;
// Handle small L values explicitly to simplify the main loop.
// g(1) = mex{g(0)} = mex{0} = 1. Move: choose 1, remove {1}, left with empty set (length 0).
if (PRECOMPUTE_LIMIT > 1) g_values[1] = 1;
// g(2) = mex{g(0)} = mex{0} = 1. Move: choose 1 or 2, remove {1, 2}, left with empty set (length 0).
if (PRECOMPUTE_LIMIT > 2) g_values[2] = 1;
// Use a set to efficiently compute MEX (Minimum Excluded value) of the Grundy values of next states
std::set<int> seen; // Stores Grundy values of states reachable in one move
for (int L = 3; L < PRECOMPUTE_LIMIT; ++L) {
seen.clear(); // Clear the set for computing g(L)
// Consider possible moves from a block of length L:
// 1. Choose an endpoint (element 1 or L): removes {1, 2} or {L-1, L}.
// The remaining block has length L-2. The Grundy value is g(L-2).
// Indices L-2 are guaranteed non-negative since L >= 3.
seen.insert(g_values[L - 2]);
// 2. Choose an interior element x (from 2 to L-1): removes {x-1, x, x+1}.
// This splits the block into two independent sub-blocks of lengths k = x-2 and L-k-3.
// The index k = x-2 runs from 0 (when x=2) to L-3 (when x=L-1).
// The resulting state corresponds to the Nim sum of the two sub-games, with Grundy value g(k) XOR g(L-k-3).
for (int k = 0; k <= L - 3; ++k) {
// Indices k and L-k-3 are guaranteed non-negative.
seen.insert(g_values[k] ^ g_values[L - k - 3]);
}
// Compute the mex (minimum excluded non-negative integer) of the set `seen`.
int mex_val = 0;
while (seen.count(mex_val)) {
mex_val++;
}
g_values[L] = mex_val; // Store the computed Grundy value for length L
}
}
// Function to get the Grundy value for a block of length L
// Uses precomputed values for small L and leverages periodicity for large L.
int get_g(long long L) {
// A block of non-positive length has Grundy value 0.
if (L <= 0) return 0;
// If L is within the precomputed range, return the stored value directly.
if (L < PRECOMPUTE_LIMIT) {
return g_values[L];
} else {
// If L is large, use the periodicity property.
// The sequence g(L) is periodic with period 34 starting from index 54.
// Map L to the equivalent index within the first full periodic segment [54, 87].
// The index mapping is: `PERIOD_START_INDEX + (L - PERIOD_START_INDEX) % PERIOD`
long long effective_idx = PERIOD_START_INDEX + (L - PERIOD_START_INDEX) % PERIOD;
return g_values[effective_idx];
}
}
int main() {
// Use fast I/O operations for potentially large input
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
// Precompute the Grundy values needed. This is done once.
precompute_g();
int N; // Number of elements in the initial set S
std::cin >> N;
// Handle the edge case of N=0 explicitly. Constraints state N >= 1, but safety first.
if (N == 0) {
// If the initial set is empty, the first player has no moves and loses immediately.
std::cout << "Second" << std::endl;
return 0;
}
// Read the elements of the set S. Use long long for potentially large values up to 10^9.
std::vector<long long> A(N);
for (int i = 0; i < N; ++i) {
std::cin >> A[i];
}
// Sort the elements. This allows us to easily identify blocks of consecutive numbers.
std::sort(A.begin(), A.end());
int nim_sum = 0; // Initialize the Nim sum (XOR sum) of the Grundy values of all blocks.
long long current_block_len = 0; // Track the length of the current consecutive block.
// Check if N > 0 before starting block processing
if (N > 0) {
current_block_len = 1; // Start length of the first block is at least 1.
// Iterate through the sorted elements (from the second element) to find blocks
for (int i = 1; i < N; ++i) {
if (A[i] == A[i-1] + 1) {
// If the current element is consecutive to the previous one, extend the current block.
current_block_len++;
} else {
// A gap is found (A[i] > A[i-1] + 1), indicating the end of the current block.
// Calculate the Grundy value for this completed block using its length.
// XOR this Grundy value into the total Nim sum.
nim_sum ^= get_g(current_block_len);
// Start a new block beginning with the current element A[i].
current_block_len = 1;
}
}
// After the loop finishes, there's one last block remaining (the one ending at A[N-1]).
// Calculate its Grundy value and XOR it into the total Nim sum.
nim_sum ^= get_g(current_block_len);
}
// Determine the winner based on the total Nim sum.
// This game is impartial. By the Sprague-Grundy theorem, the game is equivalent to a Nim pile of size `nim_sum`.
// Assuming standard normal play convention (last player to move wins), which aligns with the sample cases:
// If the Nim sum is non-zero, the first player has a winning strategy.
// If the Nim sum is zero, the second player has a winning strategy.
// Note: The problem statement "player who cannot move loses" typically describes misere play. However, analysis of sample cases suggests normal play rules apply.
if (nim_sum != 0) {
std::cout << "First" << std::endl;
} else {
std::cout << "Second" << std::endl;
}
return 0;
}
qwewe