[arc079f]Namori Grundy

来源:互联网 发布:标签价格打印软件 编辑:程序博客网 时间:2024/06/05 01:19

题目大意

一个有向弱联通环套树。
一个点的sg值等于出边连向点的sg值的mex。
试问是否有办法给每个点分配sg值?

做法

先把环上每个点i处理出两个值b[i]和c[i],b[i]表示其所连向的环上点j的sg值sg[j]如果不等于b[i],那么sg[i]=b[i],否则sg[i]=c[i]。
任找一条环边u->v,枚举sg[u]=b[u]或c[u]两种情况,有一种合法即可,否则不可能合法。

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;const int maxn=200000+10;int a[maxn],sg[maxn],cnt[maxn],b[maxn],c[maxn],p[maxn],q[maxn],h[maxn],go[maxn],nxt[maxn];bool bz[maxn],pd[maxn];int i,j,k,l,t,n,m,u,v,tot,top;bool czy;void add(int x,int y){    go[++tot]=y;    nxt[tot]=h[x];    h[x]=tot;}void dfs(int x){    int i,l=0,t=h[x];    while (t){        dfs(go[t]);        t=nxt[t];    }    t=h[x];    while (t){        q[++l]=sg[go[t]];        t=nxt[t];    }    fo(i,1,l) cnt[q[i]]++;    while (cnt[sg[x]]) sg[x]++;    fo(i,1,l) cnt[q[i]]=0;}int main(){    scanf("%d",&n);    fo(i,1,n){        scanf("%d",&a[i]);        add(a[i],i);    }    j=1;    while (1){        bz[j]=1;        if (bz[a[j]]){            u=a[j];            v=j;            break;        }        j=a[j];    }    while (1){        pd[j]=1;        if (pd[a[j]]) break;        j=a[j];    }    k=j;    while (1){        t=h[j];        top=0;        while (t){            if (!pd[go[t]]){                dfs(go[t]);                p[++top]=sg[go[t]];            }            t=nxt[t];        }        fo(i,1,top) cnt[p[i]]++;        while (cnt[b[j]]) b[j]++;        c[j]=b[j]+1;        while (cnt[c[j]]) c[j]++;        fo(i,1,top) cnt[p[i]]=0;        if (a[j]==k) break;        j=a[j];    }    sg[u]=b[u];    j=u;    while (1){        if (sg[j]==b[a[j]]) sg[a[j]]=c[a[j]];else sg[a[j]]=b[a[j]];        if (a[j]==v) break;        j=a[j];    }    if (sg[u]!=sg[v]){        printf("POSSIBLE\n");        return 0;    }    sg[u]=c[u];    j=u;    while (1){        if (sg[j]==b[a[j]]) sg[a[j]]=c[a[j]];else sg[a[j]]=b[a[j]];        if (a[j]==v) break;        j=a[j];    }    if (sg[v]!=b[u]) printf("IMPOSSIBLE\n");else printf("POSSIBLE\n");}
原创粉丝点击