这个题可以用tarjan解决,将染色后的点标记为tarjan的那个点,特判如果出发点等于目标点 答案就是1,如果要求的那个点在强连通分量里面,我们就输出其所含的cnt(也就是强连通分量所代表的环的大小)。如果目标点不在强连通分量里,我们就从该点进行扩展,记录下扩展的步数step。如果扩展到一个强连通分量内,答案就是step+该强连通分量的环的大小,break掉即可。最后输出每一个点的答案。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=100010;
int n;
int head[maxn],nnext[maxn*2],to[maxn*2];
int tot,top,index,ink;
int dfn[maxn],low[maxn],cnt[maxn],stack[maxn],ans[maxn],color[maxn];
bool b[maxn],instack[maxn];
void add(int x,int y)
{
tot++;
nnext[tot]=head[x];
head[x]=tot;
to[tot]=y;
}
void tarjan(int x)
{
dfn[x]=low[x]=++index;
stack[++top]=x;
b[x]=true;
instack[x]=true;
for(int i=head[x];i;i=nnext[i])
{
int y=to[i];
if(!b[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(instack[y])
{
low[x]=min(low[x],low[y]);
}
}
if(dfn[x]==low[x])
{
int k;
do
{
k=stack[top];
instack[k]=false;
color[k]=x;
top--;
}while(stack[top+1]!=x);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int tmp;
cin>>tmp;
add(i,tmp);
if(tmp==i) ans[i]=1;
}
for(int i=1;i<=n;i++)
{
if(!b[i])
{
tarjan(i);
}
}
for(int i=1;i<=n;i++)
{
cnt[color[i]]++;
}
for(int i=1;i<=n;i++)
{
if(cnt[color[i]]!=1)
{
ans[i]=cnt[color[i]];
}
}
for(int i=1;i<=n;i++)
{
if(ans[i]==0)
{
int step=1;
int x=i;
while(1)
{
for(int j=head[x];j;j=nnext[j])
{
x=to[j];
}
if(ans[x]!=0)
{
ans[i]=step+ans[x];
break;
}
else step++;
}
}
}
for(int i=1;i<=n;i++)
{
cout<<ans[i]<<endl;
}
return 0;
}