noip201506 Message 信息傳遞,noip201506message
試題描述:
有 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 }
View Code