[HNOI2007]分裂游戏 博弈论

题面

题面

题解

这题的思路比较特别,观察到我们的每次操作实质上是对于一颗豆子的操作,而不是对一瓶豆子的操作,因此我们要把每颗豆子当做一个独立的游戏,而它所在的瓶子代表了它的SG值。
瓶子数量很少,因此我们只需要枚举每个豆子的后继状态暴力转移即可

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 30

int T, n, ans, rnt;
int s[AC], sg[AC];
bool z[AC];

inline int read()
{
    int x = 0;char c = getchar();
    while(c > '9' || c < '0') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x;
}

int dfs(int x)
{
    if(z[x]) return sg[x];
    z[x] = true;
    bool vis[500];
    memset(vis, 0, sizeof(vis));
    for(R i = x - 1; i; i --)
        for(R j = i; j; j --) vis[dfs(i) ^ dfs(j)] = true;			
    for(R i = 0; i <= 499; i ++) if(!vis[i]) {sg[x] = i; break ;}
    return sg[x];
}

#define h(x) (n - x + 1)//查询真正的下标
void work()
{
    T = read(), sg[1] = 0, z[1] = 1;
    while(T --)
    {
        n = read(), ans = rnt = 0;
        for(R i = 1; i <= n; i ++)
            s[i] = read(), ans ^= (s[i] & 1) * dfs(n - i + 1);
        if(!ans) printf("-1 -1 -1\n");
        else for(R i = 1; i <= n; i ++)
            for(R j = i + 1; j <= n; j ++)
                for(R k = j; k <= n; k ++)
                    if((ans ^ sg[h(i)] ^ sg[h(j)] ^ sg[h(k)]) == 0) 
                    {
                        if(!rnt) printf("%d %d %d\n", i - 1, j - 1, k - 1);
                        ++ rnt;
                    }
        printf("%d\n", rnt);
    }
}

int main()
{
//	freopen("in.in", "r", stdin);
    work();
//	fclose(stdin);
    return 0;
}
posted @ 2019-01-31 14:57  ww3113306  阅读(162)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。