遊戲「並查集」
阿新 • • 發佈:2020-07-16
遊戲「並查集」
題目描述
Mirko和 Slavko 愛玩彈球戲。在一個令人激動的星期五,Mirko 和 Slavko 玩了一把彈球遊戲。Mirko 構建一個有向圖,所有頂點最多有 1 條出邊。彈球從 1個頂點出發可以沿著一條邊移動到它的鄰接點,只要它存在,而且它會繼續移動到後者的鄰接點去,直到最後到達一個找不到出邊的頂點才停下來。如果不存在這樣的點,彈球可能無限運動下去。
為了確信 Slavko理解遊戲的規則,Mirko 將發起一系列詢問,詢問的型別如下:
1 X:除非彈球陷入迴圈,彈球從 X出發,最終將在哪個點停下來。 2 X:刪除 X的出邊(保證該邊總是存在) 注意:詢問是按順序執行的。
輸入
第一行包含一個正整數 N
(1
<=N
<=300000
),表示圖的定點數。
第二行包含由空格隔開 N
個正整數,第 i
個數表示從 i
頂點可以通過出邊到達的定點編號。0
表示該點沒有出邊。
接下來的一行包含 1
個整數 Q
(1
<=Q
<=300000
),表示詢問的次數。格式如上所示。
輸出
對於第 1類詢問,輸出彈球停止時所在頂點編號,每行 1 個,按照查詢的順序輸出。如果彈球無法停止,則輸出 CIKLUS
樣例輸入
2 3 1
7
1 1
1 2
2 1
1 2
1 1
2 2
1 2
樣例輸出
CIKLUS
1
1
2
思路分析
- 因為每個節點都只有一條出邊,所以我們可以考慮並查集,並查集的祖先節點即為斷點。
- 針對有環的情況,我們只需要記錄一下跑的次數,若大於n則說明存在環
- 這題的關鍵在於刪邊這裡,我們使用倒序並查集(離線操作),另開一個數組記錄刪邊前的狀態,逐步復原即可
程式碼
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int maxn = 3e5+10; int f[maxn],ff[maxn],a[maxn][2],n; //ff陣列記錄刪邊前的狀態 int find(int x,int Clock){ if(Clock>n)return f[x] = 0; //有環 if(x==f[x])return x; return f[x] = find(f[x],Clock+1); } int main(){ scanf("%d",&n); for(int i = 1;i <= n;i++)f[i] = i; for(int i = 1;i <= n;i++){ int x;scanf("%d",&x); if(x)f[i] = ff[i] = x; } int q;scanf("%d",&q); for(int i = 1;i <= q;i++){ scanf("%d%d",&a[i][0],&a[i][1]); //操作種類和物件一併記錄 if(a[i][0]==2){ f[a[i][1]] = a[i][1]; } } for(int i = q;i >= 1;i--){ //倒序處理,保證前面的刪邊操作不會對前面的查詢造成影響 if(a[i][0]==1){ a[i][1] = find(a[i][1],0); //正常查詢 } else f[a[i][1]] = ff[a[i][1]]; //復原 } for(int i = 1;i <= q;i++){ if(a[i][0]==1){ if(a[i][1])printf("%d\n",a[i][1]); else printf("CIKLUS\n"); //a[i][1]為0說明有環 } } return 0; }