BZOJ 1022 [SHOI2008]小约翰的游戏John

1022: [SHOI2008]小约翰的游戏John

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 1635  Solved: 1036
[Submit][Status][Discuss]

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

HINT

 

【数据规模】

对于40%的数据,T ≤ 250。

对于100%的数据,T ≤ 500。

 

Source

Seerc2007

题解:系统的学了一下博弈论。。。妈妈呀。。。

具体去膜贾志豪的集训队论文好了。。。

对于这道题,zidaneandmessi

①石子数全是1的时候,是最简单的情况。堆数为奇数后手必胜,为偶数先手必胜。

②只有两堆石子数不为1,且它们的石子数相等,后手必胜,理由如下:

(1)先手若取走一堆,后手可以取走另一堆或者剩下一个,回到状态①。

(2)先手若取一堆剩一个,后手可以取走另一堆或剩一个,回到状态①。

(3)先手若取一堆剩下多于一个,后手可以在另一堆中取走相同个数的石子,重复状态②(3),直到两堆都只剩两个石子,先手必须选择②(1)或②(2)。

③只有两堆石子数不为1,且它们的石子数不相等。先手必胜。先手可以从多的一堆里取,使两堆石子相等,回到状态②。

④剩下偶数堆,由多对相等且不为1的石子堆组成。后手必胜。无论先手如何取,后手都在配对的堆中取相同个数的石子,这样可以使相等的堆两两消去,最后转为状态②。

⑤剩下偶数堆。由同样的多对不相等且不为1的石子堆组成。后手必胜。后手可以用③的方法每次使配对的两堆石子数相等,转为状态④。

⑥剩下部分是同样的多对不相等且不为1的石子堆,其余堆石子数都相等。

(1)相等的堆数为偶数,后手必胜。后手可以按⑤的方法转到状态②获胜。

(2)相等的堆数为奇数,先手必胜。先手从相等的拿走一堆后就变成了⑥(1)。

行了,就此打住!如果再这么写下去,恐怕是无止境了。写到这里,我们发现后手总是尽可能地制造配对,并想方设法地回到他最擅长的状态②。但是知道了这个并没有多大用处,因为对于抽象的问题,还是无法下手。

但是我们发现了,如果能配对成功,a1^a2^...^an=0这个等式确实是成立的,因为以异或运算满足交换律和结合律,把配对的两个异或结果算出之后,就全变成0了。

如果a1^a2^...^an结果不为0呢?那么后手就倒霉了。因为先手总能找出一种拿石子的方式,把异或变成0。设结果为x,则每一个ai异或x后肯定有增有减,不可能全都增加。至少,为结果x提供最高位1的那个ai异或x后最高位变0了,一定是减少的。只要先手把它拿成剩下x个,新局面的异或结果就是0了。

那么后手有没有翻盘的可能呢?答案是否定的。因为一旦异或结果变成0后,再拿只会让异或结果不再是0。因为如果两次都是0了,设这次拿走石子把ai=m变成了ai=n(0<n<m),a1^a2^…^a(i-1)^a(i+1)^…^an的运算结果设为s,则s^n=0,s^m=0,说明n=m=s,与n<m矛盾,这样后手不论再怎么拿,都不能避免失利。

另外,如果结果为0,但是不能配对怎么办?很简单,根据上面的结论异或结果为0和不为0是交替变化的,只要先手每次移动使结果不为0,后手就再让结果为0就可以了,这样下去直到石子剩得不多了,胜负就很明显了。所以,后手一旦失误,先手就会使局势扭转!看似随意的游戏,实际上步步为营啊!

还没完,我们推导了半天把孤零零的状态①给忘了。注意到②有个条件是相等两堆石子数不为1,所以如果所有石子数都是1,直接套用①的结论即可。

 

代码们:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<cstring>
 7 #define PAU putchar(' ')
 8 #define ENT putchar('\n')
 9 using namespace std;
10 inline int read(){
11     int x=0,sig=1;char ch=getchar();
12     while(!isdigit(ch)){if(ch=='-')sig=-1;ch=getchar();}
13     while(isdigit(ch))x=10*x+ch-'0',ch=getchar();
14     return x*=sig;
15 }
16 inline void write(int x){
17     if(x==0){putchar('0');return;}if(x<0)putchar('-'),x=-x;
18     int len=0,buf[15];while(x)buf[len++]=x%10,x/=10;
19     for(int i=len-1;i>=0;i--)putchar(buf[i]+'0');return;
20 }
21 void init(){
22     int x,t,n,T;bool f=true;
23     T=read();
24     while(T--){
25         n=read();f=true;t=0;
26         for(int i=1;i<=n;i++){
27             x=read();
28             if(x!=1)f=0;
29             t^=x;
30         }
31         if(f&&(n&1))puts("Brother");
32         else if(f)puts("John");
33         else if(t)puts("John");
34         else puts("Brother");
35     }
36     return;
37 }
38 void work(){
39     return;
40 }
41 void print(){
42     return;
43 }
44 int main(){init();work();print();return 0;}

 

posted @ 2015-07-17 17:06  AI_Believer  阅读(358)  评论(0编辑  收藏  举报