【取火柴游戏】

非常玄妙的博弈入门

好神仙的题啊

其实这就是经典的\(Nim\)问题

如果所有石子的异或和为\(0\),那么这就是一个必败状态

这个我只会感性理解一下

首先所有的石子都是\(0\),异或和肯定是\(0\),这也自然是一个必败状态

而如果异或和不是\(0\),我们设异或和为\(X\)

我们可以找到这个\(X\)的最高位,显然那些石子堆里至少有一个这一位上也是\(1\)的,设这一个石子堆是\(x_i\)

那么就有

\[x_1⊕x_2⊕x_3⊕...⊕x_i⊕...x_n=X \]

那么显然

\[x_1⊕x_2⊕x_3⊕...⊕x_i⊕...x_n⊕X=0 \]

根据异或的结合律

\[x_1⊕x_2⊕...⊕(x_i⊕X)⊕...x_n=0 \]

也就是说原来的\(x_i\)变成\(x_i⊕X\)就好了,也就是要取走\(x_i-x_i⊕X\),这样就可以使得下一步变成必败状态

由于我们取走的一定得是正数,所以得满足\(x_i-x_i⊕X>0\)

但是如果\(X=0\)的话,\(x_i⊕X=x_i\),所以\(x_i-x_i⊕X=0\),取走的是\(0\),并不满足游戏规则

所以一大波分析下来突然就觉得好有道理

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define maxn 500005
#define lowbit(x) ((x)&(-x))
inline int read()
{
	char c=getchar();
	int x=0;
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9')
		x=(x<<3)+(x<<1)+c-48,c=getchar();
	return x;
}
int n,a[maxn];
int ans,t;
int main()
{
	n=read();
	for(re int i=1;i<=n;i++) a[i]=read(),ans^=a[i];
	t=ans;
	if(!ans)
	{
		puts("lose");
		return 0;
	}
	for(re int i=1;i<=n;i++)
	{
		if(a[i]-(a[i]^ans)>0)
		{
			printf("%d %d",a[i]-(a[i]^t),i);
			putchar(10);
			a[i]=(a[i]^t);
			break;
		}
	}
	for(re int i=1;i<=n;i++)
		printf("%d ",a[i]);
	return 0;
}
posted @ 2019-01-02 12:24  asuldb  阅读(125)  评论(0编辑  收藏  举报