1. 程式人生 > 其它 >SHOI2008 小約翰的遊戲

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;
}