[HNOI2007]分裂游戏——博弈论好题
题目链接:https://www.luogu.com.cn/problem/P3185
分析:
博弈论学起来是挺有趣的,可惜我啥也不会。就因为这道题学了sg,学完之后回来看题解还是不太懂,然后又回去看sg,然后大概就懂了。这道题的思路真的很棒,对于全体而言,我们很难去用sg,因为太复杂了,但我们考虑它里面的操作:一分为二。仔细思考可以发现,对于单独一颗豆子,它可以由后面分到的两颗转移得来,这里就是最关键也是最难的地方,其它东西可以顺推得到,可是没打过sg的我只能看着别人的代码思路打,盯着代码理解半天。
代码:
#include<iostream> #include<cstdio> #include<vector> #include<cstring> #include<algorithm> using namespace std; #define int long long #define R register #define debug printf("zjy\n") inline int read(){ int a=0,b=1;char c=getchar(); while(!isdigit(c)){if(c=='-')b=-1;c=getchar();} while(isdigit(c)){a=a*10+c-'0';c=getchar();} return a*b; } const int N=22; int T,n,a[N],sum,vis[1500],sg[N],ans; void find_ans(){ int cnt=0,ii=-1,jj=-1,kk=-1; for(R int i=n;i>=1;i--){ for(R int j=i-1;j>=1;j--){ for(R int k=j;k>=1;k--){ if((ans^sg[i]^sg[j]^sg[k])==0){ cnt++; if(ii==-1){ ii=n-i;jj=n-j;kk=n-k; } } } } } printf("%lld %lld %lld\n%lld\n",ii,jj,kk,cnt); } signed main(){ sg[1]=0; for(R int i=2;i<=21;i++){ for(R int j=1;j<i;j++){ for(R int k=j;k<i;k++){ vis[sg[j]^sg[k]]=i; for(sg[i]=0;vis[sg[i]]==i;sg[i]++); } } } T=read(); while(T--){ ans=0; n=read(); for(R int i=n;i>=1;i--){ a[i]=read(); } for(R int i=n;i>=1;i--){ if(a[i]&1)ans^=sg[i]; } if(!ans)printf("-1 -1 -1\n0\n"); else find_ans(); } return 0; }