1. 程式人生 > >【取火柴遊戲】

【取火柴遊戲】

非常玄妙的博弈入門

好神仙的題啊

其實這就是經典的\(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;
}