题目分析
https://www.cnblogs.com/Robert-Yuan/p/5282669.html
Nim游戏
马上就要到高考假了,老师给大家发了许多许多卷子作为礼物,小L将各科卷子分类放好之后,小B说了一个提议:
我们来玩一个游戏吧,每次选择一堆卷子,轮流从其中拿走几张,但是不能不拿。谁没有卷子拿了,谁就输了。输了的人要帮赢了的人写一张卷子。
小L觉得海星,反正她也不想写卷子。结果小B赢了无数次,这令小L困惑不已,只见小B露出了一个神秘的微笑。
莫非是有必胜策略?小L仔细思考,发现自己果然被小B坑了:
假设第i堆试卷有
张,则S的异或和k为0的为必败状态P,否则为必胜状态N。这是由于:
1.如果当前处于N,则下一步必到P
如果当前取了第i堆的石子,使该堆变为 个石子, ,则有 ,则有 即 ,与不能不取矛盾。
2.如果当前处于P,则下一步可到N
找到所有A中的最大值A(i),则其二进制下最高为1的位一定是k二进制下最高为1的位,那么A(i)异或k小于A(i),所以让A(i)异或一下k是一个合法策略,并且这样 会变为0.
其实这种判断方法还可以推广到更一般的博弈游戏上去,成为SG定理……但是小L并不关心这些,她现在很生气要去找小B算账。
Nim游戏的变形
小B又露出一个神秘的微笑,然后说道:
这样吧,我们再玩一轮。谁拿走最后一张卷子就输了,如果你赢了就不用帮我写卷子了。
小L觉得自己已经学会了Nim游戏,一定不会输的!
……然后就输了。
难道这种游戏还有什么玄机?小L略一思索,忽然恍然大悟。
记老师很仁慈只布置了一张试卷的堆为
,有多于1张卷子的堆为
。
然后记
状态下,无
堆的记做
,一个
堆的记做
,否则记做
。同理定义
,
和
。
那么:
1.
必败。
因为有奇数个 堆,轮流拿就先手必败了。
2. 必胜。
偶数个 堆,轮流拿先手必胜。
3. 必胜。
因为如果有奇数个 堆,就把那个 堆一窝端了。否则让 堆留下一张“革命的火种”。
4. 不存在。
因为那些 堆异或和不是1就是0,显然剩下那个 堆里面试卷数量是多少都不可能让总异或和为0。
5. 必胜, 必败。
你不可能一口气让两个 堆消失,所以 只能转到 或是 ,当然 是必胜的啦,所以要尽量往 转。
而 状态下,A最大的那一堆i一定是个 堆,让其异或k。如果此时它变成了一个 堆,说明除i堆外的异或和为1。显然其他堆要么全是 堆(但那就不是 状态了),要么有两个以上的 堆,这样操作后的 堆个数不会少于两个。而如果异或后它还是一个 堆,那么 堆个数也不会少于两个,也就是说 状态绝对可以转化为 状态。
所以这么转来转去,最后的归宿一定是 状态了啊……
然后就 必胜, 必败了。
技不如人,甘拜下风。小L只能帮小B去写卷子了。
代码
#include<bits/stdc++.h>
using namespace std;
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
#define RI register int
int T,n,js,kl;
int main()
{
T=read();
while(T--) {
n=read();js=0,kl=0;
for(RI i=1;i<=n;++i) {
int x=read();kl^=x;
if(x>1) ++js;
}
if((js==0&&kl==0)||(js>0&&kl)) puts("John");
else puts("Brother");
}
return 0;
}