BZOJ 1188 [SG定理][博弈论]

来源:互联网 发布:英译汉软件下载 编辑:程序博客网 时间:2024/04/30 18:26

Description

聪聪和睿睿最近迷上了一款叫做分裂的游戏。 该游戏的规则试: 共有 n 个瓶子, 标号为 0,1,2..n1, 第 i 个瓶子中装有 pi颗巧克力豆,两个人轮流取豆子,每一轮每人选择 3 个瓶子。标号为 i,j,k, 并要保证 i<j,jk 且第 i 个瓶子中至少要有 1 颗巧克力豆,随后这个人从第 i 个瓶子中拿走一颗豆 子并在j,k中各放入一粒豆子(j 可能等于 k) 。如果轮到某人而他无法按规则取豆子,那么他将输 掉比赛。胜利者可以拿走所有的巧克力豆! 两人最后决定由聪聪先取豆子,为了能够得到最终的巧克力豆,聪聪自然希望赢得比赛。他思考了一下,发现在有的情况下,先拿的人一定有办法取胜,但是他不知道对于其他情况是否有必胜 策略,更不知道第一步该如何取。他决定偷偷请教聪明的你,希望你能告诉他,在给定每个瓶子 中的最初豆子数后是否能让自己得到所有巧克力豆,他还希望你告诉他第一步该如何取,并且为 了必胜,第一步有多少种取法? 假定 1<n21,pi10000

Solution

SG定理的基本应用吧。SGi表示第i个瓶子中的豆子,接下来就是套SG定理了。

#include <cstdio>#include <cstdlib>#include <iostream>#include <cstring>using namespace std;const int MAXN = 30000;const int N = 30;bool vis[MAXN];int a[N], SG[N];int test, G, cnt, n;int main(void) {    freopen("1.in", "r", stdin);    scanf("%d", &test);    for (int i = 1; i < 28; i++) {        memset(vis, 0, sizeof vis);        for (int j = 0; j < i; j++)            for (int k = 0; k <= j; k++)                vis[SG[j] ^ SG[k]] = true;        for (int j = 0; ; j++)            if (!vis[j]) {                SG[i] = j; break;            }    } // 预处理SG函数    while (test--) {        scanf("%d", &n); G = cnt = 0;        for (int i = 0; i < n; i++) scanf("%d", &a[i]);        for (int i = 0; i < n; i++)            if (a[i] & 1) G ^= SG[n - i - 1];        for (int i = 0; i < n; i++)            for (int j = i + 1; j < n; j++)                for (int k = j; k < n; k++)                    if ((G ^ SG[n - i - 1] ^ SG[n - j - 1] ^ SG[n - k - 1]) == 0)                        if ((++cnt) == 1) printf("%d %d %d\n", i, j, k);        if (!cnt) puts("-1 -1 -1");        printf("%d\n", cnt);    }    return 0;}
0 0
原创粉丝点击