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

来源:互联网 发布:顶速网络机顶盒 编辑:程序博客网 时间:2024/06/05 06:04

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

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

其中jiki

#include <cstdio>#define MAXN 25int 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;}
0 0