noip2015D1T1 信息传递(并查集判环)

来源:互联网 发布:多台网络打印机服务器 编辑:程序博客网 时间:2024/06/05 22:49

题目描述

有 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;}
原创粉丝点击