题目描述
有 n 个同学(编号为 1 到 n ) 正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 ii的同学的 信息传递对象是编号为 Ti 的同学。
游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信 息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉 一个人,即自己的信息传递对象)。当有人从别人口中得知自 己的生日时,游戏结束。请问该游戏一 共可以进行几轮?输入格式
输入共2行。 第1行包含1个正整数 n ,表示 n 个人。第2行包含 n 个用空格隔开的正整数 T1,T2,⋯⋯,Tn ,其中第 i 个整数 Ti 表示编号为 i的同学的信息传递对象是编号为 Ti的同学, Ti≤n 且 Ti≠i 。
数据保证游戏一定会结束。
输出格式
输出共1行,包含1个整数,表示游戏一共可以进行多少轮。
n≤200000
根本没想过dfs就可以看出我对图论的熟悉程度真的很辣鸡了……
显然显然又显然,我们可以发现这是一个判断环的问题。
如图。
根据题意,同一连通块里是不可能出现两个环的(因为每个点出度只能为1)。然而这可能并不是一个连通图,所以仍然是要判断最小环的。
并查集。判环可直接完成,另记录环的长度即可。
是有向图噢///
以及,有向图中一点的父节点是它指向的那个点。
#include<bits/stdc++.h>
using namespace std;
int n;
int fa[200002],pre[200002];//fa存第i个点的祖先节点。pre存第i个点的父节点。实则用a数组也可以。但是不知道为什么这样比较快。
int a[200002];//第i个点的指向。
int minn=10000000;
inline void read(int &x){
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
inline int find_(int x)
{
if(x==fa[x]) return x;
return(fa[x]=find_(fa[x]));
}
int main()
{
read(n);
for(int i=1;i<=n;i++)
{
read(a[i]);fa[i]=pre[i]=i;//第i个点的祖先和爹一开始都看作是自己。
}
for(int i=1;i<=n;i++)
{
int x=find_(i);//找到第i个点的祖先。
int y=find_(a[i]);//第i个点指向的点的祖先。
if(x==y)//如果他们的祖先是一样的,就说明它们在一个环中。具体为什么,看图……
{
int ans=1;
for(int j=a[i];j!=i;j=pre[j]) ans++;//计算环的长度。for循环看着很玄学,简单地说就是从a[i]->a[i]的后继.....->直至重新回到i,其中i~a[i]这条边没算,所以ans初值是1.由于fa是经过状态压缩的,所以这里不能用它。
minn=min(minn,ans);
}
fa[x]=y;//合并。
pre[x]=a[i];//这就是,呃……就是这样。
}
cout<<minn;
return 0;
}