[洛谷P2661] NOIP2015 信息传递

问题描述

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

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

输入格式

输入共 2 行。

第 1 行包含 1 个正整数 n,表示 n 个人。

第 2 行包含 n 个用空格隔开的正整数 T1, T2, … … , Tn,其中第 i 个整数Ti表示编号为 i 的同学的信息传递对象是编号为 Ti 的同学, Ti ≤ n 且 Ti ≠ i。

数据保证游戏一定会结束。

输出格式

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

样例输入

5
2 4 2 3 1

样例输出

3

数据范围

对于 30%的数据, n ≤ 200;

对于 60%的数据,n ≤ 2500;

对于 100%的数据,n ≤ 200000。

解析

题目即让我们求图中的最小环。利用每个点只有一个出边,我们可以得到如下性质:每个点最后都会进入一个环中,或者是进入死胡同。因此,我们对每个点进行DFS,如果到达了一个当前正在访问的点,说明找到了一个环,可以通过在状态中记录到达一个点时是第几步来计算环的大小。如果访问到了一个之前DFS时访问过的点,说明此后的步骤已经重复,可以结束递归。

代码

#include <iostream>
#include <cstdio>
#define N 200002
using namespace std;
int n,i,nxt[N],ans=1<<30,vis[N],cnt,num[N];
int read()
{
    char c=getchar();
    int w=0,f=1;
    while(c<'0'||c>'9'){
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c<='9'&&c>='0'){
        w=w*10+c-'0';
        c=getchar();
    }
    return w*f;
}
void dfs(int x)
{
    vis[x]=cnt;
    int y=nxt[x];
    if(vis[y]==cnt) ans=min(ans,num[x]-num[y]+1);
    else if(!vis[y]){
        num[y]=num[x]+1;
        dfs(y);
    }
}
int main()
{
    n=read();
    for(i=1;i<=n;i++) nxt[i]=read();
    for(i=1;i<=n;i++){
        if(!vis[i]){
            cnt++;
            dfs(i);
        }
    }
    cout<<ans<<endl;
    return 0;
}
上一篇:C5-垃圾粉碎机


下一篇:千万别用树套树 【题意:有多少线段完全覆盖某一线段【树状数组维护】】【模板题】