1. 程式人生 > >[BZOJ1188][HNOI2007]分裂遊戲

[BZOJ1188][HNOI2007]分裂遊戲

mit www. 兩個人 span i++ scu 一個 數據 -1

1188: [HNOI2007]分裂遊戲

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1255 Solved: 771
[Submit][Status][Discuss]

Description

聰聰和睿睿最近迷上了一款叫做分裂的遊戲。 該遊戲的規則試: 共有 n 個瓶子, 標號為 0,1,2.....n-1, 第 i 個瓶子中裝有 p[i]顆巧克力豆,兩個人輪流取豆子,每一輪每人選擇 3 個瓶子。標號為 i,j,k, 並要保證 i < j , j < = k 且第 i 個瓶子中至少要有 1 顆巧克力豆,隨後這個人從第 i 個瓶子中拿走一顆豆 子並在 j,k 中各放入一粒豆子(j 可能等於 k) 。如果輪到某人而他無法按規則取豆子,那麽他將輸 掉比賽。勝利者可以拿走所有的巧克力豆! 兩人最後決定由聰聰先取豆子,為了能夠得到最終的巧克力豆,聰聰自然希望贏得比賽。他思考 了一下,發現在有的情況下,先拿的人一定有辦法取勝,但是他不知道對於其他情況是否有必勝 策略,更不知道第一步該如何取。他決定偷偷請教聰明的你,希望你能告訴他,在給定每個瓶子 中的最初豆子數後是否能讓自己得到所有巧克力豆,他還希望你告訴他第一步該如何取,並且為 了必勝,第一步有多少種取法? 假定 1 < n < = 21,p[i] < = 10000

Input

輸入文件第一行是一個整數t表示測試數據的組數,接下來為t組測試數據(t<=10)。每組測試數據的第一行是瓶子的個數n,接下來的一行有n個由空格隔開的非負整數,表示每個瓶子中的豆子數。

Output

對於每組測試數據,輸出包括兩行,第一行為用一個空格兩兩隔開的三個整數,表示要想贏得遊戲,第一步應該選取的3個瓶子的編號i,j,k,如果有多組符合要求的解,那麽輸出字典序最小的一組。如果無論如何都無法贏得遊戲,那麽輸出用一個空格兩兩隔開的三個-1。第二行表示要想確保贏得比賽,第一步有多少種不同的取法。

Sample Input

2
4
1 0 1 5000
3
0 0 1

Sample Output

0 2 3
1
-1 -1 -1
0
可以發現每個瓶子裏對局面的影響就是奇偶 類似於翻硬幣遊戲算出局面的$sg$值 至於方案數和字典序最小的第一步,直接枚舉就行了
#include <cstdio>
#include <cstring>
const int maxn = 20 + 10;
int sg[maxn], g[100];
int main(){
    int t, n, p[maxn];
    scanf("%d", &t);
    while(t--){
        memset(g, 0, sizeof(g)); 
        scanf(
"%d", &n); for(int i = 1; i <= n; i++) scanf("%d", p + i); sg[n] = 0; for(int i = n - 1; i; i--){ for(int j = i + 1; j <= n; j++) for(int k = j; k <= n; k++) g[sg[j] ^ sg[k]] = i; for(int j = 0; ; j++) if(g[j] != i){ sg[i] = j; break; } } int Xor = 0, tot = 0; for(int i = 1; i <= n; i++) if(p[i] & 1) Xor ^= sg[i]; if(!Xor) puts("-1 -1 -1\n0"); else{ bool flag = false; for(int i = 1; i < n; i++) for(int j = i + 1; j <= n; j++) for(int k = j; k <= n; k++) if((Xor ^ sg[i] ^ sg[j] ^ sg[k]) == 0){ if(!flag){ printf("%d %d %d\n", i - 1, j - 1, k - 1); flag = true; } tot++; } printf("%d\n", tot); } } return 0; }

[BZOJ1188][HNOI2007]分裂遊戲