【AtCoder Regular Contest 079 F】Namori Grundy
构造。
题目大意:
给定一张 $n$ 个点 $n$ 条边的有向图,保证每条边入度为 $1$,保证图弱连通。
现在要给每个点设置权值。一个点的权值定义为它的出边连的点的所有权值的 $\mathrm{mex}$。
问这样的图是否存在。
题解:
最后的图的形态是一棵基环外向树。
对于树点的权值,直接模拟即可,求 $\mathrm{mex}$ 的复杂度是和度数有关的,因此总和为 $n$。
我们已经确定环上每个结点的权值至少是多少了。
接下来考虑有多少种不同情况。
- 环上的权值都相同,且环是偶环。那么每隔一个就令其增加即可。因此有解。
- 环上的权值都相同,且环是奇环。那么无解。
- 环上的权值存在不同。我们找到一个权值最小的点(必须保证严格小于它连向的环上结点),从它开始往后一直加即可。最后绕回来的时候,这个权值最小的点一定不会变化。因此有解。
Code:
#include<cstdio>
#include<queue>
const int N=2e5+5;
int p[N],head[N],cnt,n,deg[N],val[N];
bool vis[N],ur[N];
struct edge{
int to,nxt;
}e[N];
std::queue<int>q;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",p+i);
e[++cnt]=(edge){i,head[p[i]]},head[p[i]]=cnt,++deg[p[i]];
}
for(int i=1;i<=n;++i)if(!deg[i])q.push(i);
while(!q.empty()){
int u=q.front();q.pop();
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt)if(vis[e[i].to])
ur[val[e[i].to]]=1;
int&v=val[u];
while(ur[v])++v;
for(int i=head[u];i;i=e[i].nxt)ur[val[e[i].to]]=0;
if(!--deg[p[u]])q.push(p[u]);
}
int len=0,mn=0;
for(int u=1;u<=n;++u)if(!vis[u]){
for(int i=head[u];i;i=e[i].nxt)if(vis[e[i].to])
ur[val[e[i].to]]=1;
int&v=val[u];
while(ur[v])++v;
for(int i=head[u];i;i=e[i].nxt)ur[val[e[i].to]]=0;
}
for(int i=1;i<=n;++i)if(!vis[i]){
++len;if(val[p[i]]<val[i])mn=p[i];
}
printf("%sPOSSIBLE\n",(len&1)&&!mn?"IM":"");
return 0;
}