1. 程式人生 > >洛谷p2661資訊傳遞

洛谷p2661資訊傳遞

題目:

有 n 個同學(編號為 1到 n )正在玩一個資訊傳遞的遊戲。在遊戲裡每人都有一個固定的資訊傳遞物件,其中,編號為 i 的同學的資訊傳遞物件是編號為 Ti​ 的同學。

遊戲開始時,每人都只知道自己的生日。之後每一輪中,所有人會同時將自己當前所知的生日資訊告訴各自的資訊傳遞物件(注意:可能有人可以從若干人那裡獲取資訊, 但是每人只會把資訊告訴一個人,即自己的資訊傳遞物件)。當有人從別人口中得知自己的生日時,遊戲結束。請問該遊戲一共可以進行幾輪?

題目連結:資訊傳遞

分析:

昂 有向邊?跑tarjan?
剛剛做了兩道並查集的我偷偷看了一眼標籤

準備用並查集再水一道題(tarjan好長啊果斷並查集大法好啊(……)
當一個人的生日被傳回來的時候說明出現了環,這個題目很顯然要求的是最小環,遊戲結束的輪數就是最小環的長度
裴成環,堅成環,裴堅……咳
可以考慮用並查集維護這個過程,每讀入一個數據,先檢查這兩點是否已經連通(成環),若已經成環則更新最小值(最小值=兩個節點分別到祖先的距離+1(這個1指這兩個節點之間連的一條邊)),若沒有成環則連一條邊,設這條邊是A→B的有向邊,那麼顯然有A到祖先節點的距離=B到祖先節點的距離+1
更新father的過程中,記錄一個當前搜尋到的x的父節點last,先走上去到x的最老的祖先節點,邊走邊路徑壓縮,然後回溯回來的時候把值一層層傳遞下來


放一下程式碼吧(其實特別短來著):

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int cnt=0,f=1;char c;
    c=getchar();
    while(!isdigit(c)){
        if(c=='-')f=-f;
        c=getchar();
    }
    while(isdigit(c)){
        cnt=cnt*10+c-'0';
        c=getchar();
    }
    return cnt*f;
}
int n;
int t;
int fa[200005],d[200005];
int minn=1<<20;

int get_father(int x){
    if(fa[x]!=x){
        int last=fa[x];
        fa[x]=get_father(fa[x]);
        d[x]+=d[last];
    }
    return fa[x];
}
void check(int a,int b){
    int x=get_father(a);
    int y=get_father(b);
    if(x!=y){fa[x]=y;d[a]=d[b]+1;}
    else minn=min(minn,d[a]+d[b]+1);
    return ;
}
int main(){
    n=read();
    for(register int i=1;i<=n;i++)fa[i]=i,d[i]=0;
    for(register int i=1;i<=n;i++){
        t=read();
        check(i,t);
    }
    printf("%d",minn);
    return 0;
}

參考了一下洛谷第一篇題解&&神犇fsy部落格,真的是很神仙的思路誒,比tarjan又好寫又巧妙才不是因為我懶