1. 程式人生 > >Codechef Counting the important pairs 樹上差分

Codechef Counting the important pairs 樹上差分

import pair freopen http -s using times sig 影響

傳送門

題意:給出一個$N$個點、$M$條邊的無向連通圖,求有多少組無序數對$(i,j)$滿足:割掉第$i$條邊與第$j$條邊之後,圖變為不連通。$N \leq 10^5 , M \leq 3 \times 10^5$


竟然隨機化,歪果仁的思想好靈活qwq肯定是數據結構做多了

看起來很像割邊,考慮$tarjan$,但是邊散連通分量並不是很好實現,而且有很多特殊情況需要判斷,所以我們考慮另外的算法

考慮$tarjan$時建出的一棵樹。對於它來說,在一個端點在其下方、另一個端點在其上方的的返祖邊可以成為它的依靠,因為割掉它,這一條依靠邊可以代替它的功能。而對於一條返祖邊來說,割掉對於樹邊是沒有影響的,我們就定義它自己為自己的依靠。

這樣每一條邊都有自己的依靠集合。考慮兩條依賴集合相同的邊,將它們割掉之後,中間一段的點就會因為上下都沒有額外的依靠而使得圖不連通。而對於一條依賴集合為空的邊(即割邊),它選擇任何邊都可以加入貢獻。

所以我們現在需要考慮如何給某一段邊加入貢獻。然後CC的題解給出的玄學辦法是:隨機化+樹上差分+XOR

我們考慮將給一條返祖邊定權值為一個$random$出來的值$t$,然後把所有依靠它的邊的依靠集合異或上這個值,這個可以樹上差分去做。這樣所有的依靠集合就變成了一個數。然後我們判斷兩條邊的依靠集合對應的數是否相等即可。

Because 2^64 is large enough comparing to the range of N

and M, don‘t worry about the probability. :) You will get AC if you implemented correctly.——原題題解

然而我$rand() WA$了好幾發qwq

如果隨機數種子出了問題的話就看下面這種玄學rand好了

 1 #include<bits/stdc++.h>
 2 #define CC
 3 using namespace std;
 4 
 5 inline int read(){
 6     int a = 0;
 7     bool f = 0;
 8     char c = getchar();
9 while(c != EOF && !isdigit(c)){ 10 if(c == -) 11 f = 1; 12 c = getchar(); 13 } 14 while(c != EOF && isdigit(c)){ 15 a = (a << 3) + (a << 1) + (c ^ 0); 16 c = getchar(); 17 } 18 return f ? -a : a; 19 } 20 21 const int MAXN = 100010; 22 struct Edge{ 23 int end , upEd; 24 }Ed[MAXN * 6]; 25 int head[MAXN] , num[MAXN] , dep[MAXN] , fa[MAXN] , N , cntEd , cnt; 26 unsigned long long point[MAXN] , forS[MAXN * 3]; 27 bool vis[MAXN]; 28 29 #define ll unsigned long long 30 inline ll rp(){return (1ll*rand())^(1ll*rand())<<15^(1ll*rand())<<30^(1ll*rand())<<45^(1ll*rand())<<60;} 31 32 inline void addEd(int a , int b){ 33 Ed[++cntEd].end = b; 34 Ed[cntEd].upEd = head[a]; 35 head[a] = cntEd; 36 } 37 38 void dfs1(int x , int t){ 39 fa[x] = t; 40 dep[x] = dep[fa[x]] + 1; 41 vis[x] = 1; 42 for(int i = head[x] ; i ; i = Ed[i].upEd) 43 if(!vis[Ed[i].end]) 44 dfs1(Ed[i].end , x); 45 else 46 if(dep[Ed[i].end] > dep[x]){ 47 long long t = rp(); 48 point[x] ^= t; 49 point[Ed[i].end] ^= t; 50 forS[++cnt] = t; 51 } 52 } 53 54 void dfs2(int x){ 55 for(int i = head[x] ; i ; i = Ed[i].upEd) 56 if(fa[Ed[i].end] == x){ 57 dfs2(Ed[i].end); 58 point[x] ^= point[Ed[i].end]; 59 } 60 if(x != 1) 61 forS[++cnt] = point[x]; 62 } 63 64 int main(){ 65 #ifdef CC 66 freopen("TAPAIR.in" , "r" , stdin); 67 freopen("TAPAIR.out" , "w" , stdout); 68 #endif 69 70 N = read(); 71 int M = read(); 72 for(int i = 1 ; i <= M ; i++){ 73 int a = read() , b = read(); 74 addEd(a , b); 75 addEd(b , a); 76 } 77 dfs1(1 , 0); 78 dfs2(1); 79 sort(forS + 1 , forS + cnt + 1); 80 int p = 1; 81 while(p <= cnt && forS[p] == 0) 82 p++; 83 long long ans = (p - 1) * (long long)(p - 2) / 2 + (p - 1) * (M - p + 1); 84 while(p <= cnt){ 85 int beg = p; 86 while(p <= cnt && forS[p] == forS[beg]) 87 p++; 88 ans += (long long)(p - beg) * (p - beg - 1) / 2; 89 } 90 cout << ans; 91 return 0; 92 }

Codechef Counting the important pairs 樹上差分