洛谷P2575高手过招——SG函数初试

题目: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;
}
原文地址:https://www.cnblogs.com/Zinn/p/9080542.html