题目链接:https://www.luogu.com.cn/problem/P2661
我是tarjan做的,用tarjan找环,并更新最小环长度,开始输边的时候如果有起点等于终点的就直接输出1,在tarjan中的话去掉那些长度为1的强连通分量,因为他们不能构成环,所以不能用他们更新答案。这题能用并查集做的原因是每个点只有一条向外的边,所以如果两个点祖先相同,就能构成环,所以只需要在并查集的过程中更新路径长度,然后如果两个点祖先相等,说明能构成环,更新答案,下贴用tarjan做的代码。
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
const int inf=0x7fffffff;
struct node
{
int next,to;
}Node[maxn];
int n,dfn_number,cnt,top,ans;
int head[maxn],dfn[maxn],low[maxn],stc[maxn];
bool vis[maxn];
void add(int x,int y)
{
Node[++cnt].next=head[x];
Node[cnt].to=y;
head[x]=cnt;
}
void tarjan(int u)
{
dfn[u]=low[u]=++dfn_number;
vis[u]=true;
stc[++top]=u;
for(int i=head[u];i;i=Node[u].next)
{
int v=Node[u].to;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
vis[u]=false;
int len=1;
while(stc[top]!=u)
len++,vis[stc[top--]]=false;
top--;
if(len>=2)
ans=min(ans,len);
}
}
int main()
{
int n;
ans=inf;
scanf("%d",&n);
int j;
for(int i=1;i<=n;i++)
{
scanf("%d",&j);
add(i,j);
if(i==j)
{
puts("1");
return 0;
}
}
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
printf("%d\n",ans);
return 0;
}