SHOI2008 小約翰的遊戲
阿新 • • 發佈:2022-04-17
反 Nim 遊戲模板題。
題目大意
有 \(n\) 堆石子,每次可以從任意一堆石子中取走任意多個,但不能不取,取走最後一個石子的人輸。請問是否有先手必勝?
大體思路
反 Nim 遊戲的結論為:先手必勝,當且僅當:
- 每堆石子的個數均為 \(1\) 且 \(SG\) 和為 \(0\);
- 至少有一堆石子的個數 \(>1\) 且 \(SG\) 和不為 \(0\)。
證明如下:
首先考慮一種特殊情況,即 \(a_1=a_2=\cdots=a_n=1\),顯然先手必勝當且僅當 \(n\) 為偶數,而偶數個 \(1\) 的異或和恰好為 \(0\),因此 \(SG\) 和為 \(0\)。
否則,當 \(SG\)
-
若當前有至少兩堆石子的個數 \(>1\),則先手可以使得 \(SG\) 和變為 \(0\),此時情況與普通的 Nim 類似;
-
若當前有且僅有一堆石子的個數 \(>1\),先手可以選擇是否將當前堆取完,使得剩餘奇數堆的 \(1\),保證先手必勝。
當 \(SG\) 和為 \(0\) 時,情況相反,後手必勝。
完整程式碼
#include <bits/stdc++.h> using namespace std; #define rep(ii,aa,bb) for(re int ii = aa; ii <= bb; ii++) #define Rep(ii,aa,bb) for(re int ii = aa; ii >= bb; ii--) typedef long long ll; typedef unsigned long long ull; typedef double db; typedef pair<int, int> PII; const int maxn = 55; namespace IO_ReadWrite { #define re register #define gg (p1 == p2 && (p2 = (p1 = _buf) + fread(_buf, 1, 1<<21, stdin), p1 == p2) ? EOF :*p1++) char _buf[1<<21], *p1 = _buf, *p2 = _buf; template <typename T> inline void read(T &x){ x = 0; re T f=1; re char c = gg; while(c > 57 || c < 48){if(c == '-') f = -1;c = gg;} while(c >= 48 &&c <= 57){x = (x<<1) + (x<<3) + (c^48);c = gg;} x *= f;return; } inline void ReadChar(char &c){ c = gg; while(!isalpha(c)) c = gg; } template <typename T> inline void write(T x){ if(x < 0) putchar('-'), x = -x; if(x > 9) write(x/10); putchar('0' + x % 10); } template <typename T> inline void writeln(T x){write(x); putchar('\n');} } using namespace IO_ReadWrite; int T, n; int main () { read(T); while(T --) { read(n); int sum = 0, sX = 0; rep(i, 1, n) { int x; read(x); sum += x; sX ^= x; } if(sum == n) puts((sX == 0 ? "John" : "Brother")); else puts((sX ? "John" : "Brother")); } return 0; }