1. 程式人生 > >[bzoj1188][HNOI2007]分裂遊戲_博弈論

[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$。