题目
给定一张 N N N个点 M M M条边的无向连通图,然后执行 Q Q Q次操作,每次向图中添加一条边,并且询问当前无向图中“桥”的数量。
题解
- 先求出图中所有的边双,然后缩点
- 令 c [ x ] , c [ y ] c[x],c[y] c[x],c[y]为 x , y x,y x,y所属边双的编号
- 询问时若 x , y x,y x,y同属一个e-DCC则割边数不变,若不在同一个边双内,缩点后的图变成了一棵树,树上的每一条边都为原图的割边,在 x , y x,y x,y间加边那么树上从 x x x到 y y y的路径上的所有边都不再是割边,答案只需减去这些边。
- 对于统计 x , y x,y x,y路径上的边我们可以找 x , y x,y x,y的 l c a lca lca暴力往上跳,但这样的复杂度是不优秀的,我们可以并查集来维护,这样复杂度降为 O ( M + Q × N ) O(M+Q\times N) O(M+Q×N)
code
#include <bits/stdc++.h>
using namespace std;
template <class T> inline void read(T &s) {
s = 0; T w = 1, ch = getchar();
while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
s *= w;
}
const int N = 1e5 + 100;
const int M = 4e5 + 100;
const int Z = 21;
int n, m, tot, tim, q, dcc, totc, T;
int lin[N], dfn[N], low[N], c[N], linc[N], fa[N];
int f[N][Z], d[N];
bool bridge[M];
struct edge {
int next, to;
} e[M], ec[M];
inline void clean() {
tot = 1, tim = 0, dcc = 0, totc = 1;
for (int i = 1; i <= n; ++i)
c[i] = lin[i] = linc[i] = dfn[i] = low[i] = 0;
memset(bridge, false, sizeof(bridge));
}
inline void add(int from, int to) {
e[++tot].to = to;
e[tot].next = lin[from];
lin[from] = tot;
}
void tarjan(int u, int in_edge) {
dfn[u] = low[u] = ++tim;
for (int i = lin[u]; i; i = e[i].next) {
int v = e[i].to;
if (!dfn[v]) {
tarjan(v, i);
low[u] = min(low[u], low[v]);
if (low[v] > dfn[u])
bridge[i] = bridge[i ^ 1] = true;
}
else if (i != (in_edge ^ 1))
low[u] = min(low[u], dfn[v]);
}
}
void dfs(int u) {
c[u] = dcc;
for (int i = lin[u]; i; i = e[i].next) {
int v = e[i].to;
if (bridge[i] || c[v]) continue;
dfs(v);
}
}
inline void addc(int from, int to) {
ec[++totc].to = to;
ec[totc].next = linc[from];
linc[from] = totc;
}
void bfs() {
memset(d, 0, sizeof(d));
memset(f, 0, sizeof(f));
queue<int> q;
q.push(1); d[1] = 1;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = linc[u]; i; i = ec[i].next) {
int v = ec[i].to;
if (d[v]) continue;
d[v] = d[u] + 1;
f[v][0] = u;
for (int j = 1; j < 20; ++j)
f[v][j] = f[f[v][j - 1]][j - 1];
q.push(v);
}
}
}
int lca(int x, int y) {
if (d[x] < d[y]) swap(x, y);
for (int i = 19; i >= 0; --i) {
if (d[f[x][i]] >= d[y]) x = f[x][i];
}
if (x == y) return x;
for (int i = 19; i >= 0; --i) {
if (f[x][i] != f[y][i])
x = f[x][i], y = f[y][i];
}
return f[x][0];
}
inline int get(int x) {
return fa[x] == x ? x : fa[x] = get(fa[x]);
}
int main() {
freopen("1.in", "r", stdin);
while (true) {
read(n), read(m);
if (!n || !m) break;
clean();
for (int i = 1; i <= m; ++i) {
int x, y; read(x), read(y);
add(x, y); add(y, x);
}
for (int i = 1; i <= n; ++i) {
if (!dfn[i]) tarjan(i, 0);
}
for (int i = 1; i <= n; ++i) {
if (!c[i]) {
++dcc, dfs(i);
}
}
for (int i = 2; i <= tot; ++i) {
int x = e[i].to, y = e[i ^ 1].to;
if (c[x] == c[y]) continue;
addc(c[x], c[y]);
}
bfs();
for (int i = 1; i <= dcc; ++i) fa[i] = i;
int ans = dcc - 1;
read(q);
printf("Case %d:\n", ++T);
while (q--) {
int x, y; read(x), read(y);
x = c[x], y = c[y];
int p = lca(x, y);
x = get(x);
while (d[x] > d[p]) {
fa[x] = f[x][0];
ans--;
x = get(x);
}
y = get(y);
while (d[y] > d[p]) {
fa[y] = f[y][0];
ans--;
y = get(y);
}
printf("%d\n", ans);
}
puts("");
}
return 0;
}