【BZOJ 1022】[SHOI2008] 小约翰的游戏John

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1022

anti-nim游戏,证明的话,网上到处都是,不过我还想是想自己证一下,证明如下:

结论:
1)所有堆中石子的个数都为1:如果NIM和为0,则先手必胜,否则后手必胜
2)有至少一堆的石子个数大于1,如果NIM和大于0,则先手必胜,否则后手必胜

证明:
1)如果所有堆中石子数都为1:
不难看出决策也唯一,结论显然成立
2)有至少一堆的石子个数大于1:
如果NIM和大于0,显然先手可以通过调整使最高的1为1的那堆石子,使NIM和变为0,使得先手必败,故先手必胜
如果NIM和为0,则先手无论调整任意一堆石子的个数,都会使NIM和变为大于0,使得先手必胜,故先手必败
该条证明,看似是循环证明,但实际上,堆中的石子数一直在减少,所以我们可以加上如下边界条件,使得该证明严密:
不难发现,大于1的堆数在慢慢减少,总有一个时刻,只会有一堆的石子数大于1。明显,此时的NIM和不为0。
显然现在的先手可以控制留下奇数个单石子堆或是偶数个。这将直接决定游戏的胜负。也就是说谁进入“只有一个多石子堆的状态”谁胜
明显,该状态符合NIM和大于0的情况,符合该情况中描述的先手必胜。而NIM和不为0的情况,也将间接转移进入该情况。
所以这个证明过程,不会一直循环下去,且结果确定。

哎,这个证明我自己都觉得是乱搔,大家还是看论文里的证明吧:
http://oi.cyo.ng/wp-content/uploads/2016/08/sg_jiazhihao.pdf

#include<iostream>
#include<cstdio>
using namespace std;

inline int read(){
	char c=getchar(); int ret=0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret;
}

int main(){
	int T = read(); while (T--) {
		int n = read(),sg=0,tag=0;
		for (int i=1,tmp;i<=n;i++) {
			tmp = read(); sg ^= tmp;
			if (tmp > 1)  tag = 1;
		}
		if ((!sg && !tag) || (sg && tag)) printf("John\n");
		else printf("Brother\n");
	}
	return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *