【取火柴游戏】

非常玄妙的博弈入门

好神仙的题啊

其实这就是经典的(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;
}
原文地址:https://www.cnblogs.com/asuldb/p/10207933.html