试题描述:
有 n 个同学(编号为 1 到 n )正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为 T_i 的同学。游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自 己的生日时,游戏结束。请问该游戏一共可以进行几轮?
输入:
共2行,第1行包含1个正整数 n ,表示 n 个人。
第2行包含 n 个用空格隔开的正整数T_1,T_2,...,T_n ,其中第 i 个整数 T_i 表示编号为 i 的同学的信息传递对象是编号为 T_i 的同学, T_i <= n 且 T_i 不等于 i 。
数据保证游戏一定会结束。
输出:
共1行,包含1个整数,表示游戏一共可以进行多少轮。
输入示例:
5
2 4 2 3 1
输出示例:
3
数据范围:
n<=200000
--------------------------------------------分隔线--------------------------------------------------------
这题看了题就知道其实我们所要干的一件事就是找最小环……(其实我考试的时候也知道是要最小环,可不知道怎么找啊……于是乎写了一个根本错的东西但不知道怎么回事还蒙了30分……学了一年C++,就差这么一道题的70,我的省一啊……)
上面的废话选择性忽略好了……下面来说这道题的重点……
找最小环的话果断要用到强连通分量。
强连通分量:对于一个有向图的顶点的子集S,如果在S内任取两个顶点u和v,都能找到一条从u到v的路径,那么就称S是强连通的。如果在强连通的顶点集合S中加入其他任意顶点集合后,它都不再是强连通的,那么就称S是原图的一个强连通分量(SCC :Strongly Connected Component)
强连通分量的分解可以用两次简单的dfs来实现。
第一次dfs的时候,选取任意顶点作为起点,遍历所有未访问过的顶点,在回溯前给定点标号。对剩余未访问过的顶点不断重复上述过程。
完成标号后越接近图的尾部,定点的标号越小。
第二次dfs时先将所有的边反向,然后以标号最大的顶点为起点进行dfs,这样可以把图的拓扑序储存。
代码如下:
1 #include<iostream> 2 #include<cctype> 3 using namespace std; 4 const int MAXN=200000+10; 5 void read(int &x){ 6 x=0;int f=1;char ch=getchar(); 7 for(;!isdigit(ch);ch=getchar())if(ch==‘-‘)f=-1; 8 for(;isdigit(ch);ch=getchar())x=x*10+ch-‘0‘; 9 x*=f; 10 } 11 //---------------------- 12 int v[MAXN],first[MAXN],next[MAXN],e; 13 void AddEdge(int a,int b){ 14 v[++e]=b; 15 next[e]=first[a]; 16 first[a]=e; 17 } 18 19 int vr[MAXN],firstr[MAXN],nextr[MAXN],er; 20 void AddEdger(int a,int b){ 21 vr[++er]=b; 22 nextr[er]=firstr[a]; 23 firstr[a]=er; 24 } 25 //---------------------- 26 int n,tot,vs[MAXN],topo[MAXN]; 27 bool vis[MAXN]; 28 void dfs(int x){ 29 vis[x]=1; 30 for(int i=first[x];i;i=next[i]) 31 if(!vis[v[i]])dfs(v[i]); 32 vs[++tot]=x; 33 } 34 35 void dfsr(int x,int k){ 36 vis[x]=1; 37 topo[x]=k; 38 for(int i=firstr[x];i;i=nextr[i]) 39 if(!vis[vr[i]])dfsr(vr[i],k); 40 } 41 //--------------------------- 42 int cnt[MAXN]; 43 int main(){ 44 read(n); 45 for(int i=1;i<=n;i++){ 46 int tmp; 47 read(tmp); 48 AddEdge(i,tmp); 49 AddEdger(tmp,i); 50 } 51 52 memset(vis,0,sizeof(vis)); 53 for(int i=1;i<=n;i++) 54 if(!vis[i])dfs(i); 55 56 int k=1; 57 memset(vis,0,sizeof(vis)); 58 for(int i=n;i>=1;i--) 59 if(!vis[vs[i]])dfsr(vs[i],k++); 60 61 for(int i=1;i<=n;i++)cnt[topo[i]]++; 62 int ans=-1u>>1; 63 for(int i=1;i<=topo[vs[1]];i++){ 64 if(cnt[i]!=1)ans=min(ans,cnt[i]); 65 } 66 printf("%d\n",ans); 67 }