1. 程式人生 > >[bzoj1022] [SHOI2008]小約翰的遊戲John

[bzoj1022] [SHOI2008]小約翰的遊戲John

int 我們 sam 有趣 fin amp desc 單詞 sample

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

Solution

這玩意好像叫\(\rm Anti-Nim\)

先說結論,先手必勝的條件是:

  • \(SG=0\)且每堆石子數量都為\(1\)
  • 或者\(SG\ne 0\)且有任意一堆石子數量大於\(1\)

其中,\(SG\)為石子數的異或和。

對於第一種情況,異或和為\(0\)就是說有偶數堆石子,顯然先手必勝,否則先手必敗。

對於第二種情況,再分情況討論下:

  • 有且僅有一堆石子數量大於\(1\),那麽,先手必然有辦法把局面轉換成上一種情況。
  • 有兩堆及以上的石子數量大於\(1\),那麽先手必然可以把局面轉換成\(SG=0\)的情況,然後若當前只有兩堆大於\(1\)的,且後手使之變成了一堆大於\(1\)的情況,那麽先手必勝,否則後手必然會破壞\(SG=0\)的局面,然後循環往復,一定會轉換成前一種情況,此時先手必勝。

一句話代碼:

#include<bits/stdc++.h>
using namespace std;

void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

#define write(x) printf("%d\n",x)

const int maxn = 2e5+10;

void solve() {
    int n,bo=0,s=0;read(n); 
    for(int i=1,x;i<=n;i++) read(x),s^=x,bo|=(x>1);
    if((!bo&&!s)||(s&&bo)) puts("John");
    else puts("Brother");
}

int main() {
    int T;read(T);while(T--) solve();
    return 0;
}

[bzoj1022] [SHOI2008]小約翰的遊戲John