SHOI2008 小约翰的游戏
反 Nim 游戏模板题。
题目大意
有 \(n\) 堆石子,每次可以从任意一堆石子中取走任意多个,但不能不取,取走最后一个石子的人输。请问是否有先手必胜?
大体思路
反 Nim 游戏的结论为:先手必胜,当且仅当:
- 每堆石子的个数均为 \(1\) 且 \(SG\) 和为 \(0\);
- 至少有一堆石子的个数 \(>1\) 且 \(SG\) 和不为 \(0\)。
证明如下:
首先考虑一种特殊情况,即 \(a_1=a_2=\cdots=a_n=1\),显然先手必胜当且仅当 \(n\) 为偶数,而偶数个 \(1\) 的异或和恰好为 \(0\),因此 \(SG\) 和为 \(0\)。
否则,当 \(SG\) 和不为 \(0\) 时,
-
若当前有至少两堆石子的个数 \(>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;
}

浙公网安备 33010602011771号