1. 程式人生 > >【題解:洛谷2661 資訊傳遞】

【題解:洛谷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;
}