【BZOJ 1022】 [SHOI2008]小约翰的游戏John(Anti_SG)

Description

小约翰经常和他的哥哥玩一个非常有趣的游戏:桌子上有n堆石子,小约翰和他的哥哥轮流取石子,每个人取
的时候,可以随意选择一堆石子,在这堆石子中取走任意多的石子,但不能一粒石子也不取,我们规定取到最后一
粒石子的人算输。小约翰相当固执,他坚持认为先取的人有很大的优势,所以他总是先取石子,而他的哥哥就聪明
多了,他从来没有在游戏中犯过错误。小约翰一怒之前请你来做他的参谋。自然,你应该先写一个程序,预测一下
谁将获得游戏的胜利。

Input

本题的输入由多组数据组成第一行包括一个整数T,表示输入总共有T组数据(T≤500)。每组数据的第一行包
括一个整数N(N≤50),表示共有N堆石子,接下来有N个不超过5000的整数,分别表示每堆石子的数目。

Output

每组数据的输出占一行,每行输出一个单词。如果约翰能赢得比赛,则输出“John”,否则输出“Brother”
,请注意单词的大小写。

Sample Input

2
3
3 5 1
1
1

Sample Output

John
Brother

前言

定义(mex)函数的值为不属于集合(S)中的最小非负整数,即:

[mex(S)=minlbrace x brace(x otin S,x in N) ]

例如(mex(lbrace 0,2,4 brace)=1)。 对于状态(x)和它的所有(k)个后继状态(y_1,y_2,...,y_n),定义(SG)函数:

[SG(x)=mexlbrace SG (y_1),SG(y_2),...SG(y_n) brace ]

有向图游戏的和的SG函数值等于它包含的各个子游戏SG函数值的异或和,即:

[SG(G)=SG(G_1)igoplus SG(G_2)igoplus ...igoplus SG(G_n) ]

解题报告

我们先考虑剩下石堆石子数都为1的情况。当石堆的个数为奇数时,此时处于必输态(SG值=奇数个1异或=1),反则当石堆的个数为偶数时处于必赢态(SG值=0)。
如果剩下石堆石子数存在至少两个石堆大于1的情况,如果此时SG值不等于0,由于(SG(G)=SG(G_1)igoplus SG(G_2)igoplus ...igoplus SG(G_n)),设(SG(G))的二进制表示下最高位的1在第k位,你们至少存在一堆石子(SG(G_i)),它的第k位是1。显然(SG(G_i) igoplus x<SG(G_i)),我们从第(i)堆取走若干石子,相当于(SG(G_i) igoplus x),而(x igoplus x=0)。所以我们可以通过在一个石子堆里取走若干石子将总的(SG)值变为0。
如果只有一个石堆大于1,那么先手总可以让全1的石堆的个数变为奇数个。

结论

1、当剩下石堆石子数都为1的时,总SG值等于0必胜。
2、当剩下石堆石头数有一个大于1的时候,总SG值大于1必胜。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5+10;
int main(){
    int T;
    cin>>T;
    while(T--)
    {
        int n,x,ans=0;
        cin>>n;
        int flag=1;
        for(int i=1;i<=n;i++)
        {
            cin>>x;
            ans^=x;
            if(x!=1)
                flag=0;
        }
        if((flag&&!ans)||(!flag&&ans))
        {
            printf("John
");
        }
        else
            printf("Brother
");
    }
    return 0;
}

参考资料

https://wenku.baidu.com/view/25540742a8956bec0975e3a8.html
https://oi-wiki.org/math/game-theory/