100 #P1166. G58_2 尼姆(Nim)游戏*【博弈SG】Nim取石子游戏3[P1247微改]

G58_2 尼姆(Nim)游戏*【博弈SG】Nim取石子游戏3[P1247微改]

Description

【题目描述】
有一种有趣的 $2$ 人游戏:
一开始有 $N$ 堆石子(每堆石子的数量分别为 $a_i$),参与游戏双方轮流取石子;每人每次只能在其中一堆石子中取走若干颗石子(最少取 $1$ 颗)。石子取光,则游戏结束。取完石子的一方为胜。
假如参与游戏的玩家都非常聪明,问最后谁会获胜?

【输入格式】
多组测试数据,每组测试数据描述如下:
第一行一个整数 $N$($1 \le N \le 10^4$)。
第二行 $N$ 个整数 $a_i$($1 \le a_i \le 2^{30}$)。

【输出格式】
对于每组测试数据:
若先手必败,则输出 $First Lose$。
若先手必胜则输出 $First Win$ ,并且输出在游戏第一轮,先手可能采取的所有策略 $x \ y$ :表示先手从第 $x$ 堆石子中取走 $y$ 个石子。 每种策略占一行,按字典序依次输出。

【样例输入】
4
7 9 12 15
2
6 6

【样例输出】
First Win
2 5
3 11
4 13
First Lose

Hint

G58 尼姆(Nim)游戏【博弈论】

scy教学版:

#include<bits/stdc++.h>
using namespace std;

int a[11000],n,ans; int d[35]; int f[35],b[35],len; int main() { // freopen("nim.in","r",stdin); freopen("nim.out","w",stdout); d[1]=1; for(int i=2;i<=31;i++) d[i]=d[i-1]*2;//d[i]表示2^(i-1),也就是二进制从右到左的第i位的十进制值

while(scanf("%d"&#44;&amp;n)!=EOF)
{
    ans=0;
    memset(f&#44;0&#44;sizeof(f));
    for(int i=1;i&lt;=n;i++)
    {
        scanf("%d"&#44;&amp;a[i]);
        ans^=a[i];
        for(int j=1;j&lt;=31;j++)
           if( a[i]&amp;d[j] ) f[j]++;
    }
    if(ans==0) printf("First Lose\n");
    else
    {
        printf("First Win\n"); 
        len=0;for(int i=1;i&lt;=31;i++)  if( f[i]%2==1)b[++len]=i;//b数组存储全局的矛盾位 
        for(int i=1;i&lt;=n;i++)
        {
            int w=31; while( (d[w]&amp;a[i])==0 ) w--;
            if(w&lt;b[len]) continue;//表示第i堆石头无法一步改变全局为平衡状态 
            int s=0;
            for(int j=1;j&lt;=len;j++)
            {
                if(a[i]&amp; d[ b[j] ])
                     s=s+ d[ b[j] ];
                else
                     s=s- d[ b[j] ];
            }
            if(s&lt;=a[i]&amp;&amp;s&gt;0) printf("%d %d\n"&#44;i&#44;s);
        }
    }
}
return 0;

}</pre>