做了两道取石子就以为自己会了博弈论,我真是个SB
我理解的$SG$定理:
每个局面分成若干个子游戏,一个大局面的$SG$函数是每个小游戏$SG$函数的异或值
同时每个小游戏又有对应的的后继局面,那么这个游戏的$SG$函数就是所有后继局面$SG$函数的$mex$
当$SG$函数为0时,该游戏(局面)必败,否则必胜
那么这道题我们把每个石子当作一个小游戏,把一堆石子当作一个局面,每个石子位置的SG函数就是他能到达的所有后继局面的$mex$,发现两个石子只有他们位置不同时,$SG$函数才有可能不同,所以位置就是关键字,每个石子又能转移到若干个两个石子的局面,那么他的$SG$值就可以算了,最后的结果只需要枚举首次移动就好了。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 bool bo[105]; 8 int sg[25],T,n,a[25]; 9 int getsg(int x){ 10 memset(bo,0,sizeof bo); 11 for(int i=1;i<x;i++) 12 for(int j=1;j<=i;j++) 13 bo[sg[i]^sg[j]]=1; 14 for(int i=0;;i++)if(!bo[i])return i; 15 } 16 int main(){ 17 for(int i=1;i<=22;i++)sg[i]=getsg(i); 18 scanf("%d",&T); 19 while(T--){ 20 scanf("%d",&n); 21 int now=0,tot=0; 22 for(int i=1;i<=n;i++){ 23 scanf("%d",&a[i]); 24 if(a[i]&1)now^=sg[n-i+1]; 25 } 26 for(int i=1;i<=n;i++)if(a[i]) 27 for(int j=i+1;j<=n;j++) 28 for(int k=j;k<=n;k++) 29 if(!(now^sg[n-i+1]^sg[n-j+1]^sg[n-k+1])){ 30 if(!(tot++))printf("%d %d %d\n",i-1,j-1,k-1); 31 } 32 if(!tot)puts("-1 -1 -1"); 33 printf("%d\n",tot); 34 } 35 return 0; 36 }