题意:
一个棋盘有n行,每行20格子,都有一些棋子,两个人轮流进行这个操作:选择某一行一个棋子移动到该行右边第一个空的格子。不能进行的人输。问先手是否能赢。
分析:
SG函数的应用,当时自己做的时候没做出来QAQ。终结点是这一行没有棋子可以走,即0,然后逆推出其他结点的SG函数。每一行的状态看成是一个结点,然后把状态二进制压缩,1表示有棋子,0表示空格。
#include<cstdio>
#include<cstring>
using namespace std;
int sg[1<<21],vis[21];
int getSG(int x)
{
memset(vis,0,sizeof(vis));
for(int i=20;i>=0;i--){
if(x&(1<<i)){
int t=x;
for(int j=i-1;j>=0;j--)if(!(x&(1<<j))){
t^=(1<<i)^(1<<j);
vis[sg[t]]=1;break;
}
}
}
for(int i=0;i<=20;i++)if(!vis[i])return i;
}
int main()
{
memset(sg,0,sizeof(sg));
for(int i=0;i<(1<<20);i++)
sg[i]=getSG(i);
int T;scanf("%d",&T);
while(T--){
int n,m,x,ans=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&m);
int st=0;
while(m--){
scanf("%d",&x);
st|=1<<(20-x); //是从右边开始的,21这个位置为0
}
ans^=sg[st];
}
printf("%s
",ans?"YES":"NO");
}
return 0;
}