题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5795
题意:给出n堆石子,双方轮流从从一堆中拿出至少一个棋子或把其中一堆分成三堆非空的石子堆,问先手是否有必胜策略。
想法:明显是Nim游戏类型,那么我们需要考虑给出的分石子这步特殊的操作。之前在hihoCoder上面做过分成两堆的题目,当时是找的规律,所以比赛时就直接打了个表。发现只有在被8整除时或者被8除余7时,SG值才会有变化。那很直接的每步对应的异或下SG值就好了。
代码如下:
#include <cstdio>
#include <iostream>
#include <cstring>
const int MAXN = 100 + 10;
int sg[MAXN];
bool vis[MAXN];
int main(){
/*
freopen("3.out", "w", stdout);
sg[0] = 0;
for(int i = 1; i < MAXN; ++i){
memset(vis, false, sizeof(vis));
for(int j = 0; j < i; ++j){
vis[sg[j]] = true;
}
for(int j = 1; j <= i; ++j){
for(int k = 1; k <= i; ++k){
for(int l = 1; l <= i; ++l){
if(k + j + l == i){
vis[sg[j] ^ sg[k] ^ sg[l]] = true;
}
}
}
}
for(int j = 0; j < 300; ++j){
if(!vis[j]){
sg[i] = j;
break;
}
}
}
for(int i = 0 ; i < MAXN; ++i){
if(sg[i] != i){
printf("sg[%d] = %d\n", i, sg[i]);
}
}*/
int cas;
scanf("%d", &cas);
while(cas--){
int n;
scanf("%d", &n);
int ans = 0, x;
for(int i = 0; i < n; ++i){
scanf("%d", &x);
if(x % 8 == 0){
ans ^= x - 1;
}else if(x % 8 == 7){
ans ^= x + 1;
}else{
ans ^= x;
}
}
if(!ans){
puts("Second player wins.");
}else{
puts("First player wins.");
}
}
return 0;
}