【题解】NOIP 2015 信息传递(tarjan 强连通分量)

时间:2022-12-16 23:34:51

题目描述

有 n 个同学(编号为 1 到 n )正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为 Ti 的同学。

游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自 己的生日时,游戏结束。请问该游戏一共可以进行几轮?

输入格式

输入共2行。 第1行包含1个正整数 n ,表示 n 个人。
第2行包含 nn 个用空格隔开的正整数 T1,T2,⋯⋯,Tn,其中第 ii 个整数 TiTi 表示编号为 ii 的同学的信息传递对象是编号为 Ti 的同学, Ti≤n 且 Ti≠i。
数据保证游戏一定会结束。

输出格式

输出共1行,包含1个整数,表示游戏一共可以进行多少轮。

分析与解

这道题可以用tarjan算法求出联通块,在弹栈时记录弹了几次,认为一个点不是强连通分量,输出最小的数就可以了。
AC代码如下

#include<iostream>
#include<stack>
#include<vector>
const int maxn = 2e5+5;

using namespace std;

int low[maxn],dfn[maxn],ans=maxn,n,index,vis[maxn];
vector<int> Edge[maxn];
stack<int> S;//tarjan 用栈

void tarjan(int u)
{
low[u]=dfn[u]=++index;
vis[u] = 1;
S.push(u);
for(int i=0;i<Edge[u].size();i++)
{
int v = Edge[u][i];
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(vis[v])
low[u] = min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
int count = 0,now;
while(1)
{
count++;//记录点的个数
now = S.top();
vis[now] = 0;
S.pop();
if(now == u)
break;
}
if(count > 1)//只有一个点不视为强连通分量
{
ans = min(count,ans);
}
}
}

int main()
{
cin >> n;
for(int i=1;i<=n;i++)
{
int x;
cin >> x;
Edge[i].push_back(x);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])
tarjan(i);
}
cout << ans;
return 0;
}