题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5724
【题意】Alice 和 Bob 在一个n*20的棋盘上下棋,每行有m个棋子(m<=20)。两人轮流移动棋子,每次只能且必须移动一个棋子,棋子只能移动到该棋子右边最近的一个不超过棋盘边界的空位。当没有棋子可以移动的时候判输。
【分析】简单的博弈题。因为每行位子的数目是固定的,可以用二进制数表示一行所有可能的状态,求出所有状态的SG值,最后每行异或一下得到答案。
【代码】
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
#define MAXN 100100
int sg[1<<20];
vector<int> v;
bool cpr(int x,int y)
{
return x<y;
}
int main(){
int T,n,m,p;
cin>>T;
int len=1<<20;
for(int i=1;i<len;++i){
int fla=0;
for(int j=1;(1<<j)<=i;++j)
if(i==(1<<j)-1){
fla=1;
break;
}
if(fla){
sg[i]=0;
continue;
}
v.clear();
for(int j=1;(1<<j)<=i;++j)
if((i&(1<<j))!=0){
for(int k=j-1;k>=0;k--){
if((i&(1<<k))==0){
v.push_back(sg[i^(1<<j)^(1<<k)]);
break;
}
}
}
int x=0;
sort(v.begin(),v.end(),cpr);
for(int j=0;j<v.size();++j){
if(v[j]>x)
break;
if(v[j]==x)
x++;
}
sg[i]=x;
}
while(T--){
int ans=0;
scanf("%d",&n);
for(int i=0;i<n;++i){
scanf("%d",&m);
int a,tmp=0;
for(int j=0;j<m;++j){
scanf(" %d",&a);
tmp^=(1<<(20-a));
}
ans^=sg[tmp];
}
if(ans)
cout<<"YES\n";
else
cout<<"NO\n";
}
}