【取火柴游戏】
非常玄妙的博弈入门
好神仙的题啊
其实这就是经典的\(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;
}