BZOJ1188 [HNOI2007]分裂游戏(SG函数)

传送门
拿到这道题就知道是典型的博弈论,但是却不知道怎么设计它的SG函数。看了解析一类组合游戏这篇论文之后才知道这道题应该怎么做。
这道题需要奇特的模型转换。即把每一个石子当做一堆石子,且原来在第i堆的石子(从0开始标号)的石子个数为n-i-1,这样题目就转化成了每次取一堆石子,并放回两个比这一堆的石子个数少的石堆。这样,我们就可以有序的递推sg函数值了。
即:

sg(i)=mex({sg[j]  xor  sg[k]})

其中 ji ki

#include <cstdio>
#define MAXN 25
int sg[MAXN], n, a[MAXN];
bool used[MAXN];
void init() {
    for(int i = 1; i < MAXN; ++ i) {
        for(int j = 0; j < MAXN; ++ j)used[j] = 0;
        for(int j = 0; j < i; ++ j)
            for(int k = 0; k <= j; ++ k)
                used[sg[j]^sg[k]] = 1;
        for(int j = 0; j < MAXN; ++ j) if(!used[j]) {
            sg[i] = j; break;
        }
    }
}
int main() {
    init(); int T; scanf("%d", &T);
    while(T --) {
        scanf("%d", &n);
        int ans = 0, cnt = 0;
        for(int i = 0; i < n; ++ i) {
            scanf("%d", &a[i]);
            if(a[i] & 1) ans ^= sg[n-i-1];
        }
        for(int i = 0; i < n; ++ i) {
            if(!a[i]) continue;
            for(int j = i+1; j < n; ++ j) {
                if(!a[j]) continue;
                for(int k = j; k < n; ++ k) {
                    if(!a[k]) continue;
                    if((ans ^ sg[n-i-1] ^ sg[n-j-1] ^ sg[n-k-1]) == 0) {
                        if(!cnt) printf("%d %d %d\n", i, j, k);
                        ++ cnt;
                    }
                }
            }
        }
        if(!cnt) puts("-1 -1 -1");
        printf("%d\n", cnt);
    }
    return 0;
}

你可能感兴趣的:(函数,sg,bzoj)