動態圖連通性(線段樹分治+按秩合並並查集)


在考場上遇到了這個的板子題,,,所以來學習了一下線段樹分治 + 帶撤銷的並查集。

題目大意是這樣的:有m個時刻,每個時刻有一個加邊or撤銷一條邊的操作,保證操作合法,沒有重邊自環,每次操作后輸出當前圖下所有聯通塊大小的乘積。

 

首先觀察到如果沒有撤銷操作,那么直接用並查集就可以維護,每次合並的時候乘上要合並的兩個並查集大小的逆元,然后乘上合並之后的大小即可。

那么來考慮撤銷,觀察到如果並查集不帶路徑壓縮,應該是可以處理撤銷操作的。

 

但我們並不能直接做,因為並查集的撤銷必須按順序來,就相當於每次合並的時候將一條邊壓入棧,撤銷的時候也只能從棧頂彈出。如果不按順序是維護不了的。

對於每個加邊操作而言,我們將它和離它最近的那個撤銷操作匹配(默認第m + 1個時刻有一個撤銷所有邊的操作)

 

假設加邊操作出現在第l個時刻,撤銷操作在第r個時刻,那么對於這個二元組而言,它的作用是使得加入的那條邊在[l, r-1]的時間內出現。

於是我們考慮用線段樹來處理這個東西,我們可以將這條邊掛在線段樹上,相當於在線段樹上區間修改,將這條邊掛在[l, r -1]的區間上,

因此每條邊都會被拆分成log個區間,分別掛在線段樹上的對應位置,然后當我們經過線段樹上的一個節點時,我們就將這個節點上掛的邊都加入到當前圖中,相當於我們遍歷了整個線段樹,

線段樹上的每個葉子節點都是一個詢問(一個時刻),因此當我們遍歷到一個葉子節點時,我們就會擁有當前時刻應該擁有的邊。

當我們離開一個點時,我們就將在這個點上加入的邊撤銷,因為我們加邊是從上往下遍歷時一個一個加,而撤銷是回溯時一個一個撤銷,所以撤銷是按順序撤銷的,所以並查集就可以維護了。

 

感覺講的有一點混亂。。。。。

大概是一個區間上掛了一條邊表示這條邊在[l, r]中的所有時刻都出現了,當處理一個單點的時候,需要將到達這個單點的路徑上經過的所有區間中的邊都加入圖中,撤銷時按順序撤銷。

可能需要畫個圖之類的,應該還是比較好理解的。

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 101000
  5 #define ac 500000
  6 #define maxn 2010000
  7 #define p 1000000007
  8 #define LL long long
  9 
 10 int n, m, top, cnt, w;
 11 LL rnt = 1;
 12 int Head[maxn], Next[maxn], date[maxn], tot;
 13 int father[AC], up[AC];
 14 LL inv[AC], Size[AC], ans[AC];
 15 struct line{
 16     int x, y;
 17     friend bool operator < (line a, line b)
 18     {
 19         if(a.x != b.x) return a.x < b.x;
 20         else return a.y < b.y;
 21     }
 22 }road[AC];
 23 
 24 struct node{
 25     int fa, x;
 26 }s[AC];
 27 
 28 map<line, int> MAP;
 29 
 30 inline int read()
 31 {
 32     int x = 0;char c = getchar();
 33     while(c > '9' || c < '0') c = getchar();
 34     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
 35     return x;
 36 }
 37 
 38 inline void add(int f, int w){
 39     date[++tot] = w, Next[tot] = Head[f], Head[f] = tot;
 40 }
 41 
 42 inline int find(int x){
 43     while(father[x] != x) x = father[x];
 44     return x;
 45 }
 46 
 47 inline void link(int &ret, int x, int y)
 48 {
 49     int fx = find(x), fy = find(y);
 50     if(fx == fy) return ;
 51     ++ ret;//只有連接了才要加ret
 52     if(Size[fx] > Size[fy]) swap(fx, fy);
 53     rnt = rnt * inv[Size[fx]] % p * inv[Size[fy]] % p;
 54     father[fx] = fy, Size[fy] += Size[fx];
 55     s[++top] = (node){fy, fx};
 56     rnt = rnt * Size[fy] % p;
 57 }
 58 
 59 inline void cut()
 60 {
 61     node x = s[top --];//撤銷
 62     rnt = rnt * inv[Size[x.fa]] % p;
 63     father[x.x] = x.x, Size[x.fa] -= Size[x.x];//斷開連接
 64     rnt = rnt * Size[x.fa] % p * Size[x.x] % p;//注意刪除一個點之后要把父親修改為自己,而不是0
 65 }
 66 
 67 void solve(int x, int l, int r)//當前區間編號,區間范圍
 68 {
 69     int now, ret = 0;
 70     for(R i = Head[x]; i ; i = Next[i])
 71     {
 72         now = date[i];
 73         link(ret, road[now].x, road[now].y);
 74     }
 75     int mid = (l + r) >> 1;
 76     if(l == r) ans[l] = rnt;
 77     if(l != r) solve(x * 2, l, mid), solve(x * 2 + 1, mid + 1, r);
 78     for(R i = 1; i <= ret; i ++) cut();
 79 }
 80 
 81 void pre()
 82 {
 83     n = read(), m = read();
 84     inv[0] = inv[1] = 1;
 85     for(R i = 1; i <= n; i ++) father[i] = i, Size[i] = 1;
 86     for(R i = 2; i <= n; i ++) 
 87         inv[i] = (p - p / i) * inv[p % i] % p;
 88 }
 89 
 90 void change(int x, int l, int r, int ll, int rr)
 91 {
 92     if(l == ll && r == rr){add(x, w); return ;}
 93     int mid = (l + r) >> 1;
 94     if(rr <= mid) change(x * 2, l, mid, ll, rr);
 95     else if(ll > mid) change(x * 2 + 1, mid + 1, r, ll, rr);         
 96     else change(x * 2, l, mid, ll, mid), change(x * 2 + 1, mid + 1, r, mid + 1, rr);
 97 }
 98 
 99 void work()
100 {    
101     int tmp;
102     for(R i = 1; i <= m; i ++)
103     {
104         int opt = read(), a = read(), b = read();
105         if(a > b) swap(a, b);
106         if(!MAP[(line){a, b}]) 
107             MAP[(line){a, b}] = ++ cnt, tmp = cnt, road[cnt] = (line){a, b};
108         else tmp = MAP[(line){a, b}];//獲取編號
109         if(opt == 1) up[tmp] = i;//存下這條邊的出現時間
110         else w = tmp, change(1, 1, m, up[tmp], i - 1), up[tmp] = 0; 
111     }
112     for(R i = 1; i <= cnt; i ++)
113         if(up[i]) w = i, change(1, 1, m, up[i], m);
114     solve(1, 1, m);
115     for(R i = 1; i <= m; i ++) printf("%lld\n", ans[i]);
116 }
117 
118 int main()
119 {
120     //freopen("in.in", "r", stdin);
121     pre();
122     work();
123     //fclose(stdin);
124     return 0;
125 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM