結果
| 問題 |
No.3120 Lower Nim
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2025-04-24 14:55:24 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
WA
|
| 実行時間 | - |
| コード長 | 3,927 bytes |
| コンパイル時間 | 2,325 ms |
| コンパイル使用メモリ | 198,952 KB |
| 実行使用メモリ | 26,480 KB |
| 平均クエリ数 | 144.84 |
| 最終ジャッジ日時 | 2025-04-24 14:55:35 |
| 合計ジャッジ時間 | 9,809 ms |
|
ジャッジサーバーID (参考情報) |
judge1 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 1 |
| other | AC * 5 WA * 38 |
ソースコード
// Interactive Game Solver
// ───────────────────────
// 最上位ビット戦略(MSB)で常に nim XOR を 0 に保つ。
// K を越える最小の 2 冪 L を求め,Ai % L で Nim を張り直すと
// 通常 Nim と同値になることを利用する。
#include <bits/stdc++.h>
using namespace std;
using int64 = long long;
/*--- 2 の冪で K を超える最小 L を返す ------------------------------------*/
static inline int64 nextPow2(int64 K) {
int64 L = 1;
while (L <= K) L <<= 1;
return L;
}
/*--- XOR(Ai % L) を計算 ---------------------------------------------------*/
static inline int64 xorMod(const vector<int64>& A, int64 L) {
int64 x = 0;
for (int64 v : A) x ^= (v % L);
return x;
}
/*--- 1 手で負け局面に移す手 (idx,x) を求める.存在しないとき {-1,-1} --------*/
static pair<int,int64> winningMove(const vector<int64>& A, int64 K) {
int64 L = nextPow2(K);
int64 nim = xorMod(A, L);
if (nim == 0) return {-1, -1}; // 負け局面:任意合法手を後で作る
int64 msb = 1LL << (63 - __builtin_clzll(nim)); // nim の MSB
for (int i = 0; i < (int)A.size(); ++i) {
int64 mod = A[i] % L;
if (mod & msb) {
int64 target = mod ^ nim; // mod → target (< mod)
int64 x = mod - target; // 1 ≤ x ≤ L/2 ≤ K
if (1 <= x && x <= K && x <= A[i]) return {i, x};
}
}
return {-1, -1}; // 通常来ない
}
/*--- 適当な合法手(負け局面用) ------------------------------------------*/
static pair<int,int64> fallbackMove(const vector<int64>& A, int64 K) {
for (int i = 0; i < (int)A.size(); ++i)
if (A[i] >= 1 && 1 <= K) return {i, 1};
return {-1, -1}; // すべて 0 ならゲームは終わっている
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
/*--- 初期入力 ---------------------------------------------------------*/
int N;
if (!(cin >> N)) return 0;
vector<int64> A(N);
for (auto &v : A) cin >> v;
/*--- 先手 or 後手 -----------------------------------------------------*/
int64 initial_xor = 0;
for (auto v : A) initial_xor ^= v;
bool myTurn = (initial_xor != 0); // xor≠0 なら先手必勝
if (myTurn) {
cout << "First" << endl << flush;
} else {
cout << "Second" << endl << flush;
}
int64 K = (1LL << 60); // 10^100 の代わり(Ai ≤ 1e4 なので十分)
/*--- ゲームループ -----------------------------------------------------*/
while (true) {
if (!myTurn) { // ―― ジャッジの手番 ――
int idx_input;
int64 x_input;
if (!(cin >> idx_input >> x_input)) return 0; // i, x
int ret;
cin >> ret; // 状態
if (ret != 0) return 0; // 勝敗確定
int idx = idx_input - 1;
A[idx] -= x_input;
if (A[idx] == 0) { // 山が空なら削除
A.erase(A.begin() + idx);
}
K = x_input;
}
/*―― 自分の手番 ――*/
pair<int,int64> mv = winningMove(A, K);
if (mv.first == -1) mv = fallbackMove(A, K); // xor==0 など
int idx = mv.first;
int64 x = mv.second;
cout << (idx + 1) << ' ' << x << endl << flush;
A[idx] -= x;
if (A[idx] == 0) {
A.erase(A.begin() + idx);
}
K = x;
int ret;
cin >> ret; // ゲーム状態
if (ret != 0) return 0; // 1: 勝利, -1: 敗北
myTurn = false; // 次はジャッジ
}
}