[bzoj1188][HNOI2007]分裂遊戲_博弈論
阿新 • • 發佈:2018-12-12
分裂遊戲 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$。