[bzoj1188]分裂游戏——SG函数 大佬们的博客 Some Links

题目大意:

有n个碗里面装了ai粒糖果,然后每次一个人可以选择一组 i < j k 然后把第i和碗里面的糖果个数-1,后面两个碗里的糖果数量+1,到最后操作不了的人便输了。

思路:

这题的思路是真的巧妙,不愧是我大HNOI的题目。。。
发现满足不了条件只有一种情况,就是所有的糖果都在最后一个碗里面。但是SG函数的使用必须要有一个确定的状态,我们不可能去把每一个碗里面的每一种状态都记下来,这不现实。
然后就是这题的巧妙之处了,我们将每一个糖果看作一个单独的游戏,就是将这个糖果从第i个位置移动到第n个位置,其实也就是一堆个数为 n i 的石子等你来取。
如果每一次只增加一个糖果的话就很好办了,但是是这是增加了两个糖果,相当于增加了又新加了一堆石子。换一个角度去想,新加的这一堆石子反正最后总是要取走的,转移后的状态便是后面两个糖果所构成的游戏的和。数据这么小,上SG就完事了。

/*======================
 * Auhtor : ylsoi
 * Problem : bzoj1188
 * Algorithm : SG
 * Time : 2018.6.2
 *=====================*/ 
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<climits>
using namespace std;
void File(){
    freopen("bzoj1188.in","r",stdin);
    freopen("bzoj1188.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAX
const int maxn=21+10;
const int maxm=10000+10;
int T,n,a[maxn],SG[maxn],ans;
bool in[maxm];
int main(){
    File();
    SG[0]=0;
    REP(i,1,21){
        mem(in);
        REP(j,0,i-1)REP(k,j,i-1){
            in[SG[j]^SG[k]]=1;
        }
        REP(j,0,21*21)if(!in[j]){
            SG[i]=j;
            break;
        }
    }
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        REP(i,1,n)scanf("%d",&a[i]);
        ans=0;
        REP(i,1,n)if(a[i]%2)
            ans^=SG[n-i];
        if(!ans)puts("-1 -1 -1\n0");
        else{
            int cnt=0,ti,tj,tk,flag=0;
            REP(i,1,n)REP(j,i+1,n)REP(k,j,n){
                if((ans^SG[n-i]^SG[n-j]^SG[n-k])==0){
                    ++cnt;
                    if(!flag)ti=i-1,tj=j-1,tk=k-1,flag=1;
                }
            }
            printf("%d %d %d\n",ti,tj,tk);
            printf("%d\n",cnt);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/80552209