[bzoj1188][HNOI2007]分裂游戏_博弈论
分裂游戏 bzoj-1188 HNOI-2007
题目大意:题目链接。
注释:略。
想法:
我们发现如果一个瓶子内的小球个数是奇数才是有效的。
所以我们就可以将问题变成了一个瓶子里最多只有一个球球。
设$sg(x)$表示位置为$x$的小球的$sg$值。
显然通过$n^2$暴力转移即可。
求出了所有点的$sg$值之后,把所有有奇数个小球的位置用$SG$定理异或起来即可啦。
Code:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define N 25 using namespace std; inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;} int rd() {int x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=nc(); return x;} int a[26],sg[26]; void pre() { bool mark[20001]; sg[1]=0; for(int i=2;i<=25;i++) { memset(mark,0,sizeof(mark)); for(int j=1;j<i;j++) for(int k=1;k<=j;k++) mark[sg[j]^sg[k]]=1; for(int j=0;;j++) if(!mark[j]){sg[i]=j;break;} } // for(int i=0;i<=22;i++) printf("%d ",sg[i]); puts(""); } int main() { int t,n; pre(); scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); int ans=0,tot=0; for(int i=1;i<=n;i++) if(a[i]&1)ans^=sg[n-i+1]; for(int i=1;i<=n;i++) if(a[i]) for(int j=i+1;j<=n;j++) for(int k=j;k<=n;k++) if((ans^sg[n-i+1]^sg[n-j+1]^sg[n-k+1])==0) { tot++; if(tot==1)printf("%d %d %d\n",i-1,j-1,k-1); } if(!tot)printf("-1 -1 -1\n"); printf("%d\n",tot); } return 0; }
小结:博弈论真的考验思维。注意$sg$的更新时定义的是位置而不是距离,所以在统计答案的时候需要用$n-i+1$。
| 欢迎来原网站坐坐! >原文链接<