BZOJ1188 [HNOI2007]分裂遊戲
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
24
1 0 1 5000
3
0 0 1
Sample Output
0 2 31
-1 -1 -1
0
題解
我們把每個石子看做一個單獨的遊戲(因為它們是獨立的),那麽其SG值只取決於它到最右邊的距離。
設其$SG$值為$SG_i$,其中$i$表示石子到右邊界的位置,那麽$SG_i = mex\left\{SG_j xor SG_k \mid 0 \leq j, k < i\right\}
由於$1 \leq n < 21$,我們可以暴力$O(n^3)$計算所有$SG$值。找到方案及統計方案數時$O(n^3)$枚舉第一步的走法即可。
代碼:
#include <cstdio> const int N = 25; int SG[N], a[N]; int f[10000]; int main() { int T, n, sg = 0; SG[0] = 0; for (int i = 1; i < N; ++i) { for (int j = 0; j < i; ++j) for (int k = 0; k < i; ++k) f[SG[j] ^ SG[k]] = i; for (SG[i] = 0; f[SG[i]] == i; ++SG[i]); } scanf("%d", &T); while (T--) { scanf("%d", &n); sg = 0; for (int i = n - 1; ~i; --i) { scanf("%d", &a[i]); if (a[i] & 1) sg ^= SG[i]; } if (!sg) { printf("-1 -1 -1\n0\n"); continue; } int ans = 0; for (int i = n - 1; ~i; --i) for (int j = i - 1; ~j; --j) for (int k = j; ~k; --k) if (a[i] && !(sg ^ SG[i] ^ SG[k] ^ SG[j]) && !(ans++)) printf("%d %d %d\n", n - i - 1, n - j - 1, n - k - 1); printf("%d\n", ans); } return 0; }
BZOJ1188 [HNOI2007]分裂遊戲