题目:https://www.luogu.org/problemnew/show/P2575
第一次用SG函数解决问题,有许多不熟练的地方;
试图按自己的理解写一个dfs,结果错了(连题都没读对,以为是像跳棋一样跳),这样的话用dfs从左往右推就不行了呢;
附上自己的错误尝试:

#include<iostream> #include<cstdio> #include<cstring> using namespace std; int maxn=200005; int T,n,p,ans,g[2100000]; int dfs(int x) { if(g[x])return g[x]-1; int ret=0; for(int i=20;i>=1;i--) if(x&(1<<i)) { if((x&(1<<(i-1)))&&(x&(1<<(i-2)))==0&&i-2>=0) { int k=x-(1<<i)+(1<<(i-2)); // if(!dfs(k)) // { // g[x]=2; // return 1; // } ret^=dfs(k); } if((x&(1<<(i-1)))==0&&i-1>=0) { int k=x-(1<<i)+(1<<(i-1)); // if(!dfs(k)) // { // g[x]=2; // return 1; // } ret^=dfs(k); } } if(!ret)g[x]=1; else g[x]=2; // printf("%d %d ",x,g[x]-1); return g[x]-1; } int main() { scanf("%d",&T); while(T--) { memset(g,0,sizeof g); ans=0; scanf("%d",&n); for(int i=1,m;i<=n;i++) { p=0; scanf("%d",&m); for(int j=1,x;j<=m;j++) { scanf("%d",&x); x--; p|=(1<<(20-x)); } // if(ans)continue; // if(dfs(p)==0)ans=1; ans^=dfs(p); } if(ans)printf("YES "); else printf("NO "); } return 0; }
下面是正解,也就是个SG函数的模板;
状压一下每一行,先预处理出来所有状态的SG函数值(注意vis[里面是SG函数值]),然后每次询问异或一下就可以了,还挺简明的。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int T,n,m,ans,sg[1<<20]; bool vis[25]; void init(int x) { memset(vis,0,sizeof vis); int w=0; for(int i=1;i<=20;i++)//从小到大对应从右往左 { int t=(1<<(i-1)); if(x&t) { if(i>1&&(x&(t>>1))==0) vis[sg[x-t+(t>>1)]]=1; // if(i>2&&(x&(t>>1))&&(x&(t>>2))==0) // vis[sg[x-t+(t>>2)]]=1;//读错题 if((x&(t>>1))&&w) vis[sg[x-t+(1<<(w-1))]]=1; } else w=i; } int k=0;//求mex while(vis[k])k++; sg[x]=k; } int main() { scanf("%d",&T); for(int i=0;i<=(1<<20)-1;i++)init(i); while(T--) { ans=0;//! scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&m); int p=0; for(int j=1,x;j<=m;j++) { scanf("%d",&x); p|=(1<<(20-x)); } ans^=sg[p]; } if(ans)printf("YES "); else printf("NO "); } return 0; }