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

posted @ 2018-12-12 08:52  JZYshuraK_彧  阅读(152)  评论(0编辑  收藏  举报