POJ 3694 (tarjan缩点+LCA+并查集)

Posted wangwangyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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:
", ++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
", ans);
166         }
167     }
168     return 0;
169 }

 

以上是关于POJ 3694 (tarjan缩点+LCA+并查集)的主要内容,如果未能解决你的问题,请参考以下文章

poj3694 双连通分量+lca

POJ 3694 Network (tarjan + LCA)

poj3694(lca + tarjan求桥模板)

POJ3694 Network(Tarjan双联通分图 LCA 桥)

LCA目录

[Nowcoder] network | Tarjan 边双连通分量 | 缩点 | LCA倍增优化 | 并查集