SHOI2008 小约翰的游戏

反 Nim 游戏模板题。

题目大意

\(n\) 堆石子,每次可以从任意一堆石子中取走任意多个,但不能不取,取走最后一个石子的人输。请问是否有先手必胜?

大体思路

反 Nim 游戏的结论为:先手必胜,当且仅当:

  1. 每堆石子的个数均为 \(1\)\(SG\) 和为 \(0\)
  2. 至少有一堆石子的个数 \(>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;
}
posted @ 2022-04-17 11:59  Mars_Dingdang  阅读(44)  评论(0)    收藏  举报