洛谷P2575高手過招——SG函數初試
阿新 • • 發佈:2018-05-24
open isp tdi .org 第一次 mes 結果 aps AR
題目: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\n",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\n"); else printf("NO\n"); } 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\n"); else printf("NO\n"); } return 0; }
洛谷P2575高手過招——SG函數初試