POJ3694 Network - Tarjan + 并查集

时间:2022-02-11 15:55:21

Description

给定$N$个点和 $M$条边的无向联通图, 有$Q$ 次操作, 连接两个点的边, 问每次操作后的图中有几个桥

Solution

首先Tarjan找出边双联通分量, 每个双联通分量缩成一个点, 就构成了一棵树, 每一条树边都是桥。

执行连$u, v$ 边时, 用并查集跳到没有桥的深度最浅并且深度比$lca$深的点, 将它与父节点的并查集合并, 再接着跳。

每跳一次, 桥的数量就减少$1$。

另外感谢Iowa 神犇提醒我$cut$数组要开$M << 1$, 不是 $N << 1$, 拯救了$RE$崩溃的我呜呜

Code

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define rd read()
using namespace std; const int N = 1e5 + 1e4;
const int M = 2e5 + 1e4; int n, m, dfn[N], low[N], cnt;
int head[N], tot;
int Head[N], Tot;
int col[N], col_num, father[N], ans;
int top[N], son[N], size[N], f[N], dep[N];
int cut[M << ]; struct edge {
int nxt, to;
}E[M << ], e[M << ]; int read() {
int X = , p = ; char c = getchar();
for(; c > '' || c < ''; c = getchar()) if(c == '-') p = -;
for(; c >= '' && c <= ''; c = getchar()) X = X * + c - '';
return X * p;
} void add(int u, int v) {
e[++tot].to = v;
e[tot].nxt = head[u];
head[u] = tot;
} void Add(int u, int v) {
E[++Tot].to = v;
E[Tot].nxt = Head[u];
Head[u] = Tot;
} void dfs1(int u) {
size[u] = ;
for(int i = Head[u]; i; i = E[i].nxt) {
int nt = E[i].to;
if(nt == f[u]) continue;
f[nt] = u;
dep[nt] = dep[u] + ;
dfs1(nt);
size[u] += size[nt];
if(size[nt] > size[son[u]]) son[u] = nt;
}
} void dfs2(int u) {
if(!son[u]) return;
top[son[u]] = top[u];
dfs2(son[u]);
for(int i = Head[u]; i; i = E[i].nxt) {
int nt = E[i].to;
if(nt == f[u] || nt == son[u]) continue;
top[nt] = nt;
dfs2(nt);
}
} int LCA(int x, int y) {
for(; top[x] != top[y];) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
x = f[top[x]];
}
if(dep[x] < dep[y]) swap(x, y);
return y;
} int get(int x) {
return father[x] == x? x : father[x] = get(father[x]);
} void merg(int x, int y) {
x = get(x); y = get(y);
father[x] = y;
} int ch(int x) {
return ((x + ) ^ ) - ;
} void tarjan(int u, int pre) {
dfn[u] = low[u] = ++cnt;
for(int i = head[u]; i; i = e[i].nxt) {
if(i == ch(pre)) continue;
int nt = e[i].to;
if(!dfn[nt]) {
tarjan(nt, i);
low[u] = min(low[u], low[nt]);
if(low[nt] > dfn[u]) {
cut[ch(i)] = cut[i] = ;
ans++;
}
}
else low[u] = min(low[u], dfn[nt]);
}
} void dfs(int u) {
col[u] = col_num;
for(int i = head[u]; i; i = e[i].nxt) {
int nt = e[i].to;
if(col[nt] || cut[i]) continue;
dfs(nt);
//blo[col_num].push_back(nt);
}
} void work() {
ans = Tot = tot = cnt = col_num = ;
memset(Head, , sizeof(Head));
memset(head, , sizeof(head));
memset(cut, , sizeof(cut));
memset(dfn, , sizeof(dfn));
memset(col, , sizeof(col));
memset(son, , sizeof(son));
memset(size, , sizeof(size));
for(int i = ; i <= m; ++i) {
int u = rd, v = rd;
add(u, v); add(v, u);
}
for(int i = ; i <= n; ++i) if(!dfn[i]) tarjan(i, );
for(int i = ; i <= n; ++i) if(!col[i]) {
++col_num; dfs(i);
}
for(int i = ; i <= tot; ++i) {
int x = e[i].to, y = e[ch(i)].to;
if(col[x] == col[y]) continue;
Add(col[x], col[y]);
}
for(int i = ; i <= col_num; ++i) father[i] = i;
dfs1();
top[] = ; dfs2();
int T = rd;
for(; T; T--) {
int u = col[rd], v = col[rd], lca = LCA(u, v);
u = get(u); v = get(v);
while(dep[u] > dep[lca]) {
merg(u, f[u]);
u = get(u);
ans --;
}
while(dep[v] > dep[lca]) {
merg(v, f[v]);
v = get(v);
ans --;
}
printf("%d\n", ans);
}
} int main()
{
for(int i = ; ; ++i) {
n = rd; m = rd;
if(!n && !m) return ;
printf("Case %d:\n", i);
work();
putchar('\n');
}
}