POJ 3694 Network (tarjan + LCA)
题目链接:http://poj.org/problem?id=3694
题意是给你一个无向图n个点,m条边,将m条边连接起来之后形成一个图,有Q个询问,问将u和v连接起来后图中还有多少个桥。
首先用tarjan标记点的low和dfn值,那么u和v相连的边是桥的条件是dfn[u] < low[v](说明v与u不在一个连通分量里面,v无法通过回溯到达u点,画个图模拟会清楚)。那么bridge[v]++表示u与v相连的边是桥(若是标记bridge[u]++,则最后的答案可能会出错,亲测)。要是u和v相连,不属于同一个连通分量的话会形成一个环路,那么环路里所有的桥都没有了,所以用LCA将u和v一边找公共祖节点,一边消除桥。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 using namespace std; 6 const int MAXN = 2e5 + 5; 7 struct data { 8 int next , to; 9 }edge[MAXN * 4]; 10 int head[MAXN] , block[MAXN] , low[MAXN] , dfn[MAXN] , st[MAXN] , dep[MAXN] , par[MAXN] , bridge[MAXN]; 11 int top , ord , sccnum , cont , ans; 12 bool instack[MAXN]; 13 14 void init() { 15 memset(head , -1 , sizeof(head)); 16 memset(low , 0 , sizeof(low)); 17 memset(dfn , 0 , sizeof(dfn)); 18 memset(instack , false , sizeof(instack)); 19 memset(bridge , 0 , sizeof(bridge)); 20 top = ord = sccnum = cont = ans = 0; 21 } 22 23 inline void add(int u , int v) { 24 edge[cont].next = head[u]; 25 edge[cont].to = v; 26 head[u] = cont++; 27 } 28 29 void tarjan(int u , int p , int d) { 30 low[u] = dfn[u] = ++ord; 31 st[++top] = u; 32 instack[u] = true; 33 par[u] = p; 34 dep[u] = d; 35 for(int i = head[u] ; ~i ; i = edge[i].next) { 36 int v = edge[i].to; 37 if(v == p) 38 continue; 39 if(!dfn[v]) { 40 tarjan(v , u , d + 1); 41 low[u] = min(low[v] , low[u]); 42 if(dfn[u] < low[v]) { 43 bridge[v]++; 44 ans++; 45 } 46 } 47 else if(instack[v]) { 48 low[u] = min(low[u] , dfn[v]); 49 } 50 } 51 if(low[u] == dfn[u]) { 52 int v; 53 sccnum++; 54 do { 55 v = st[top--]; 56 instack[v] = false; 57 block[v] = sccnum; 58 }while(u != v); 59 } 60 } 61 62 void lca(int u , int v) { 63 while(dep[u] < dep[v]) { 64 if(bridge[v]) { 65 ans--; 66 bridge[v]--; 67 } 68 v = par[v]; 69 } 70 while(dep[u] > dep[v]) { 71 if(bridge[u]) { 72 ans--; 73 bridge[u]--; 74 } 75 u = par[u]; 76 } 77 while(v != u) { 78 if(bridge[u]) { 79 bridge[u]--; 80 ans--; 81 } 82 if(bridge[v]) { 83 bridge[v]--; 84 ans--; 85 } 86 u = par[u]; 87 v = par[v]; 88 } 89 } 90 91 int main() 92 { 93 int n , m , u , v , q , Case = 1; 94 while(~scanf("%d %d" , &n , &m) && (n || m)) { 95 init(); 96 while(m--) { 97 scanf("%d %d" , &u , &v); 98 add(u , v); 99 add(v , u); 100 } 101 tarjan(1 , -1 , 0); 102 scanf("%d" , &q); 103 printf("Case %d:\n" , Case++); 104 while(q--) { 105 scanf("%d %d" , &u , &v); 106 if(block[u] == block[v]) { 107 printf("%d\n" , ans); 108 continue; 109 } 110 lca(u , v); 111 printf("%d\n" , ans); 112 } 113 putchar('\n'); 114 } 115 }