POJ 3694 (tarjan缩点+LCA+并查集)
好久没写过这么长的代码了,题解东哥讲了那么多,并查集优化还是很厉害的,赶快做做前几天碰到的相似的题。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 using namespace std; 5 6 const int N = 1e5 + 50, M = 2e5 + 50; 7 int head[N], ver[M * 2], Next[M * 2]; 8 int dfn[N], low[N], n, m, tot, num; 9 bool bridge[M * 2]; 10 11 void add(int x, int y) 12 { 13 ver[++tot] = y, Next[tot] = head[x], head[x] = tot; 14 } 15 void tarjan(int x, int in_edge) 16 { 17 dfn[x] = low[x] = ++num; 18 for(int i = head[x]; i; i = Next[i]) 19 { 20 int y = ver[i]; 21 if(!dfn[y]) 22 { 23 tarjan(y, i); ///节点 和 入边 24 low[x] = min(low[x], low[y]); 25 26 if(low[y] > dfn[x]) 27 { 28 bridge[i] = bridge[i ^ 1] = true; 29 } 30 } 31 else if(i != (in_edge ^ 1)) ///搜索树上的边 32 { 33 low[x] = min(low[x], dfn[y]); 34 } 35 } 36 } 37 int c[N], dcc; 38 void dfs(int x) 39 { 40 c[x] = dcc; 41 for(int i = head[x]; i; i = Next[i]) 42 { 43 int y = ver[i]; 44 if(c[y] || bridge[i]) continue; 45 dfs(y); 46 } 47 } 48 const int maxn = N; 49 ///加边 50 int cnt, h[maxn]; 51 struct edge 52 { 53 int to, pre, v; 54 } e[maxn << 1]; 55 void add(int from, int to, int v) 56 { 57 cnt++; 58 e[cnt].pre = h[from]; ///5-->3-->1-->0 59 e[cnt].to = to; 60 e[cnt].v = v; 61 h[from] = cnt; 62 } 63 ///LCA 64 int dist[maxn]; 65 int dep[maxn]; 66 int anc[maxn][33]; ///2分的父亲节点 67 void dfs(int u, int fa) 68 { 69 for(int i = h[u]; i; i = e[i].pre) 70 { 71 int v = e[i].to; 72 if(v == fa) continue; 73 dist[v] = dist[u] + e[i].v; 74 dep[v] = dep[u] + 1; 75 anc[v][0] = u; 76 dfs(v, u); 77 } 78 } 79 void LCA_init(int n) 80 { 81 for(int j = 1; (1 << j) < n; j++) 82 for(int i = 1; i <= n; i++) if(anc[i][j-1]) 83 anc[i][j] = anc[anc[i][j-1]][j-1]; 84 } 85 int LCA(int u, int v) 86 { 87 int log; 88 if(dep[u] < dep[v]) swap(u, v); 89 for(log = 0; (1 << log) < dep[u]; log++); 90 for(int i = log; i >= 0; i--) 91 if(dep[u] - (1 << i) >= dep[v]) u = anc[u][i]; 92 if(u == v) return u; 93 for(int i = log; i >= 0; i--) 94 if(anc[u][i] && anc[u][i] != anc[v][i]) 95 u = anc[u][i], v = anc[v][i]; 96 return anc[u][0]; 97 } 98 int fa[N]; 99 int Find(int x) 100 { 101 if(x == fa[x]) return x; 102 return fa[x] = Find(fa[x]); 103 } 104 int main() 105 { 106 int kase = 0; 107 while(scanf("%d %d", &n, &m) != EOF) 108 { 109 if(n == 0 && m == 0) break; 110 tot = 1, dcc = 0, cnt = 0; 111 for(int i = 1; i <= n; i++) head[i] = 0, dfn[i] = 0, low[i] = 0, c[i] = 0, h[i] = 0; 112 for(int i = 1; i <= m * 2 + 1; i++) ver[i] = 0, Next[i] = 0, bridge[i] = false; 113 for(int i = 1; i <= m; i++) 114 { 115 int x, y; scanf("%d %d", &x, &y); 116 add(x, y), add(y, x); 117 } 118 ///求桥 119 for(int i = 1; i <= n; i++) 120 { 121 if(!dfn[i]) tarjan(i, 0); 122 } 123 ///求边双连通分量 124 for(int i = 1; i <= n; i++) 125 { 126 if(!c[i]) 127 { 128 ++dcc; 129 dfs(i); 130 } 131 } 132 ///缩点 133 for(int i = 2; i <= tot; i++) 134 { 135 int x = ver[i ^ 1], y = ver[i]; 136 if(c[x] == c[y]) continue; 137 add(c[x], c[y], 1); 138 } 139 dfs(1, 0); 140 LCA_init(dcc); 141 ///并查集初始化 142 for(int i = 1; i <= dcc; i++) fa[i] = i; 143 int Q; scanf("%d", &Q); 144 int ans = dcc - 1; 145 printf("Case %d:\n", ++kase); 146 while(Q--) 147 { 148 int x, y; scanf("%d %d", &x, &y); 149 x = c[x], y = c[y]; 150 int p = LCA(x, y); 151 x = Find(x); 152 while(dep[x] > dep[p]) 153 { 154 fa[x] = anc[x][0]; 155 ans--; 156 x = Find(x); 157 } 158 y = Find(y); 159 while(dep[y] > dep[p]) 160 { 161 fa[y] = anc[y][0]; 162 ans--; 163 y = Find(y); 164 } 165 printf("%d\n", ans); 166 } 167 } 168 return 0; 169 }