【SG函数】BZOJ1188(HNOI2007)[分裂游戏]题解

来源:互联网 发布:淘宝申请售后能成功吗 编辑:程序博客网 时间:2024/06/05 03:45

题目概述

游戏规则:共有 n 个瓶子,第 i 个瓶子中装有 pi 颗巧克力豆,两个人轮流取豆子,每一轮每人选择瓶子 i,j,k,i<jk 且第 i 个瓶子中至少要有 1 颗巧克力豆,随后这个人从第 i 个瓶子中拿走一颗豆子并在 j,k 中各放入一粒豆子。如果轮到某人而他无法按规则取豆子,那么他将输掉比赛。问先手第一次操作后使对手必败的方案数。

解题报告

Orz Lynstery,什么都会。我们要把每颗豆子独立开来,那么一颗在 i 的豆子就可以变成在 j,k 的两颗豆子,所以SG函数 SG(i)=mex{SG(j) xor SG(k)|i<jk}

那么如果 i 上有 pi 颗豆子,就是 piSG(i) 异或起来,暴力枚举一下就可以知道答案了。

ps:最好倒着存SG函数,这样就不用每次都计算了。

示例程序

#include<cstdio>using namespace std;const int maxn=21;int te,n,a[maxn+5],sg[maxn+5],vis[maxn*maxn+5],ans;int main(){    freopen("program.in","r",stdin);    freopen("program.out","w",stdout);    for (int i=1;i<=maxn;i++)    {        for (int j=i-1;j;j--) for (int k=j;k;k--) vis[sg[j]^sg[k]]=i;        while (vis[sg[i]]==i) sg[i]++;    }    for (scanf("%d",&te);te;te--)    {        scanf("%d",&n);for (int i=n;i;i--) scanf("%d",&a[i]);ans=0;        for (int i=n;i;i--) if (a[i])        for (int j=i-1;j;j--)        for (int k=j;k;k--)        {            a[i]--;a[j]++;a[k]++;int nim=0;            for (int t=1;t<=n;t++) nim^=(a[t]&1)?sg[t]:0;            if (nim==0) {if (!ans) printf("%d %d %d\n",n-i,n-j,n-k);ans++;}            a[i]++;a[j]--;a[k]--;        }        if (!ans) puts("-1 -1 -1");printf("%d\n",ans);    }    return 0;}
原创粉丝点击