消息传递(最小SCC)

来源:互联网 发布:java aes加解密 编辑:程序博客网 时间:2024/06/05 10:19

problem

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

输入格式

输入共2行

第 1 行包含 1 个正整数n(2≤n≤200000),表示 n 个人。

第 2 行包含 n 个用空格隔开的正整数T1,T2,...,Tn,其中第i个整数Ti表示编号为i的同学,Ti<=nTii,数据保证游戏一定会结束

输出格式

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

样例输入

5
2 4 2 3 1

样例输出

3


思路

注意到每一轮同时将信息进行传递,并且每个人的信息传递者是固定的。
因为自己的信息一开始只能由自己产生,那就意味着,将每个人的传递组合在一起,看成有向图,如果存在环(题目保证存在),自己的信息就被传了回来,而进行的轮数即为这个环的结点数


一点思考:本题200000的数据量,花费空间大概为18428 KB,大概4000000的int数组,即大概20*200000
在传递的过程中,每个人可能会记录下很多别人的信息,但这些信息不都是我想要的,我只需要关注自己的信息什么时候能回来,因此并不需要花费更多空间。


本题采用了tarjan算法求解SCC,相关可以参考 Tarjan算法求解强连通分量(SCC)


代码示例

#include<bits/stdc++.h>using namespace std;#define M(a,b) memset(a,b,sizeof(b))const int maxn=200010;int pre[maxn],sccno[maxn],dfs_clock,scc_cnt;vector<int> G[maxn];stack<int> S;int n;int Hash[maxn];int dfs(int u){    int lowu=pre[u]=++dfs_clock;    S.push(u);    for(int i=0;i<G[u].size();++i){        int v=G[u][i];        if(!pre[v]){            int lowv=dfs(v);            lowu=min(lowu,lowv);        }        else if(!sccno[v]){            lowu=min(lowu,pre[v]);        }    }    if(lowu==pre[u]){        ++scc_cnt;        while(true){            int x=S.top();S.pop();            sccno[x]=scc_cnt;            if(x==u) break;        }    }    return lowu;}void find_scc(int n){    M(pre,0);    M(sccno,0);    dfs_clock=scc_cnt=0;    for(int i=1;i<=n;++i) if(!pre[i]) dfs(i);}int main(){    ios::sync_with_stdio(false);    //freopen("read.txt","r",stdin);    cin>>n;    int tp;    for(int i=0;i<n;++i){//n条边        cin>>tp;        G[i+1].push_back(tp);    }    find_scc(n);//n个点    for(int i=1;i<=n;++i){//统计每个环的大小        Hash[sccno[i]]++;    }    sort(Hash+1,Hash+n+1);    for(int i=1;i<=n;++i){//取最小的,由于时间允许,所以直接sort了...        if(Hash[i]>1){            cout<<Hash[i]<<endl;            break;        }    }    return 0;}
原创粉丝点击