題目連接:https://vjudge.net/problem/POJ-3694node
題目:給定一個連通圖,求橋的個數,每次查詢,加入一條邊,問加入這條邊後還有多少個橋。ios
思路:tarjan + 並查集 + lca(樸素)函數
先用tarjan縮點(成環縮點),並存下橋,把每一個scc都存下一個源點(源點(boss):以這個點表明這個scc)。優化
用存下的橋,用並查集從新建圖,爲了方便以後的操做,並查集創建一顆樹,dfn小的在上,dfn大的在下。ui
lca,用每一個點的boss的dfn去跑lca,由於咱們建樹的方法,總會遇到公共的dfn祖先,把這些點都存下,spa
最後把這些boss點的dfn都變成祖先的dfn值,這樣優化了重複的lca,以後輸出答案便可。.net
1 #include <iostream>
2 #include <cstdio>
3 #include <algorithm>
4 #include <vector>
5 using namespace std; 6 #define pb push_back
7
8 const int N = (int)5e5+10; 9 int n,m,tot,tim,top,scc,ans;//點,邊,鏈式前向星,時間戳,棧,連通數
10 int head[N],dfn[N],low[N],scc_no[N],s[N],fa[N],boss[N]; 11 //鏈式前向星,dfn,low,聯通塊編號,棧,父節點,源點
12 struct node{ 13 int to; 14 int nxt; 15 }e[N << 1]; 16 struct _cut{ 17 int x,y; 18 }; 19 vector<_cut> cut;//橋
20 vector<int> poi;//lca
21
22 void init(){ 23 for(int i = 0; i <= n; ++i){ 24 head[i] = -1; 25 dfn[i] = 0; 26 } 27 cut.clear(); 28 scc = tim = tot = 0; 29 } 30
31 inline void add(int u,int v){ 32 e[tot].to = v; 33 e[tot].nxt = head[u]; 34 head[u] = tot++; 35 } 36
37 void tarjan(int now,int pre){ 38 dfn[now] = low[now] = ++tim; 39 s[top++] = now; 40
41 int to,pre_cnt = 0; 42 for(int o = head[now]; ~o; o = e[o].nxt){ 43 to = e[o].to; 44 if(to == pre && pre_cnt == 0) { pre_cnt = 1; continue; } 45 if(!dfn[to]){ 46 tarjan(to,now); 47 low[now] = min(low[now],low[to]); 48 if(dfn[now] < low[to]) cut.pb(_cut{now,to}); 49 } 50 else if(low[now] > dfn[to]) low[now] = dfn[to]; 51 } 52
53 if(dfn[now] == low[now]){ 54 int p; 55 ++scc; 56 fa[now] = now; boss[scc] = now;//記錄該scc的源點
57 do{ 58 p = s[--top]; 59 scc_no[p] = scc; 60 }while(now != p); 61 } 62 } 63 //獲得源點函數
64 inline int _boss(int x){ 65 return boss[scc_no[x]]; 66 } 67 //重建圖 boss進行並查集
68 void rebuild(){ 69 ans = cut.size(); 70 int x,y; 71 for(int i = 0; i < ans; ++i){ 72 x = _boss(cut[i].x); 73 y = _boss(cut[i].y); 74 //dfn上小,下大的樹
75 if(dfn[x] > dfn[y]) swap(x,y); 76 fa[y] = x; 77 } 78 } 79
80 void lca(int x,int y){ 81 int fax = _boss(x); 82 int fay = _boss(y); 83 if(dfn[fax] == dfn[y]) return; 84
85 poi.pb(fax); poi.pb(fay); 86 while(dfn[fax] != dfn[fay]){ 87 while(dfn[fax] > dfn[fay]){ 88 --ans; 89 fax = fa[fax]; 90 poi.pb(fax); 91 } 92 while(dfn[fax] < dfn[fay]){ 93 --ans; 94 fay = fa[fay]; 95 poi.pb(fay); 96 } 97 } 98
99 int n = poi.size();//全部boss點dfn改變爲祖先的dfn
100 for(int i = 0; i < n; ++i) dfn[poi[i]] = dfn[fax]; 101 poi.clear(); 102 } 103
104 int main(){ 105
106 int _case = 0; 107 while(~scanf("%d%d",&n,&m) && (n+m)){ 108 init(); 109 int u,v; 110 for(int i = 0; i < m; ++i){ 111 scanf("%d%d",&u,&v); 112 add(u,v); add(v,u); 113 } 114
115 tarjan(1,1); 116 rebuild(); 117
118 int q; 119 scanf("%d",&q); 120 printf("Case %d:\n",++_case); 121 while(q--){ 122 scanf("%d%d",&u,&v); 123 lca(u,v); 124 printf("%d\n",ans); 125 } 126 } 127
128
129
130 return 0; 131 }