[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;
}

 

posted @ 2020-08-24 09:01  zjy1412  阅读(81)  评论(0编辑  收藏  举报