【題解:洛谷2661 資訊傳遞】
傳送門:(https://www.luogu.org/problemnew/show/P2661)
題目描述
有 n個同學(編號為 1 到 n )正在玩一個資訊傳遞的遊戲。在遊戲裡每人都有一個固定的資訊傳遞物件,其中,編號為 ii 的同學的資訊傳遞物件是編號為 T_i 的同學。
遊戲開始時,每人都只知道自己的生日。之後每一輪中,所有人會同時將自己當前所知的生日資訊告訴各自的資訊傳遞物件(注意:可能有人可以從若干人那裡獲取資訊, 但是每人只會把資訊告訴一個人,即自己的資訊傳遞物件)。當有人從別人口中得知自 己的生日時,遊戲結束。請問該遊戲一共可以進行幾輪?
輸入輸出格式
輸入格式:
共2行。
第1行包含1個正整數 n ,表示 n 個人。
第2行包含 n 個用空格隔開的正整數 T_1,T_2,⋯⋯T_n。其中第 i 個整數 T_i
表示編號為 i的同學的資訊傳遞物件是編號為 T_i 的同學,
輸出格式:
11個整數,表示遊戲一共可以進行多少輪。
輸入樣例1:
5
2 4 2 3 1
輸出樣例1:
3
說明
樣例解釋
遊戲的流程如圖所示。當進行完第 3輪遊戲後, 4號玩家會聽到 2 號玩家告訴他自己的生日,所以答案為 3。當然,第 3 輪遊戲後, 2號玩家、3 號玩家都能從自己的訊息來源得知自己的生日,同樣符合遊戲結束的條件。
對於 30%的資料, n ≤ 200;
對於 60%的資料, n ≤ 2500;
對於 100%的資料, n ≤ 200000。
分析
題非常簡單,就是要求一個有向圖中的最小環的邊權總和。
由於圖中每一條邊權值都為1,而這個有向圖裡每一個人都只有1個資訊傳遞的物件,也就是說每個點的出度都為1。由這一點不難發現這個環裡點的數量等於邊的數量。
值得一提的是,在這個有向圖中,環必然是一個強連通分量,可以tarjan出來。
所以演算法如下:
1.建圖。
2.tarjan,注意原圖可能由多個連通塊組成,所以要跑一邊全圖。
3.找最小環中有多少節點。
程式碼:
#include<bits/stdc++.h> using namespace std; inline int read(){ char ch; while((ch=getchar())<'0'||ch>'9') ; int ans=ch-48; while((ch=getchar())>='0'&&ch<='9') ans=ans*10+ch-48; return ans; } inline void write(int x){ if(x<0) putchar('-') ,x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } int n; int head[200001],low[200001],dfn[200001],size[200001]; int scc[200001],vis[200001]; int cnt=0,num=0,color=0; struct node{ int u,v,nt; }e[200001]; inline void add(int u,int v){ cnt++; e[cnt]=(node){u,v,head[u]}; head[u]=cnt; } stack<int>st; inline void tarjan(int x){ low[x]=dfn[x]=++num; st.push(x),vis[x]=1; for(int i=head[x];i;i=e[i].nt){ int v=e[i].v; if(!dfn[v]){ tarjan(v); low[x]=min(low[x],low[v]); } else if(vis[v]) low[x]=min(low[x],dfn[v]); } if(low[x]==dfn[x]){ color++; while(1){ int t=st.top();st.pop(); vis[t]=0;scc[t]=color; size[color]++; if(t==x) break; } } } int main(){ n=read(); int v; for(int i=1;i<=n;i++){ v=read(); add(i,v); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); sort(size+1,size+color+1); for(int i=1;i<=color;i++){ if(size[i]>1) { write(size[i]); break; } } return 0; }