题目大意
有一个由n个点和m条边组成的无向联通图。
现在有Q个操作,每次操作可以在点x,y之间连一条边。
问你每次操作后有多少个多少个桥(即删掉后就会使图不联通的边)。
解题思路
根据边双联通的定义,我们知道将边双联通分量缩点后的图,其中的边即为桥。
我们将这个图缩点,就变成了一棵树。
而每次在两个不同的边双联通分量x,y之间加边后,就出现了一个包含x,y的环,其中原先这颗树上x,y的树上最短路径就不在是边。
所以对于每个x,y,我们用倍增求出它的最近公共祖先z,其中包含的桥的数量为x的深度+y的深度-2*z的深度。
但是需要注意的是有些边会被算过多次,我们可以将这些边标记一下,然后每次直接dfs走一遍x和y到z的路径进行统计。
而我这里用了并查集进行优化,分别跑一遍x到z和y到z的路径,将每个点都指向父节点,然后进行路径合并,下次再次统计的时候会直接跳过。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#define rep(x, l, r) for(int x = l; x <= r; x++)
#define repd(x, r, l) for(int x = r; x >= l; x--)
#define clr(x, y) memset(x, y, sizeof(x))
#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define MAXN 300005
#define MAXM 1200005
#define fi first
#define se second
#define SZ(x) ((int)x.size())
using namespace std;
typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int p = 10000007;
int lowbit(int x){ return x & -x; }
int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p; } return x; }
int n, t, cnt, cnt2, num, dcc;
int head[MAXN], nxt[MAXM], to[MAXM];
int head2[MAXN], nxt2[MAXM], to2[MAXM];
int dfn[MAXN], low[MAXN], c[MAXN];
bool bge[MAXN];
int deep[MAXN], fa[MAXN][25], fa2[MAXN];
void init(){
cnt = 0;
clr(head, -1);
cnt2 = 0;
clr(head2, -1);
clr(dfn, 0);
clr(low, 0);
clr(bge, 0);
clr(c, 0);
clr(fa, 0);
clr(deep, 0);
dcc = 0;
num = 0;
}
void addedge(int u, int v){
nxt[cnt] = head[u];
head[u] = cnt;
to[cnt] = v;
cnt++;
}
void tarjan(int u, int in_edge){
dfn[u] = low[u] = ++num;
for(int e = head[u]; e != -1; e = nxt[e]){
int v = to[e];
if(!dfn[v]){
tarjan(v, e);
low[u] = min(low[u], low[v]);
if(dfn[u] < low[v]) bge[e] = bge[e ^ 1] = 1;
}
else if(e != (in_edge ^ 1))
low[u] = min(low[u], dfn[v]);
}
}
void dfs(int u){
c[u] = dcc;
for(int e = head[u]; e != -1; e = nxt[e]){
int v = to[e];
if(c[v] || bge[e]) continue;
dfs(v);
}
}
void addedge2(int u, int v){
nxt2[cnt2] = head2[u];
head2[u] = cnt2;
to2[cnt2] = v;
cnt2++;
}
void dfs2(int u){
for(int e = head2[u]; e != -1; e = nxt2[e]){
int v = to2[e];
if(!deep[v]){
fa[v][0] = u;
deep[v] = deep[u] + 1;
dfs2(v);
}
}
}
void init2(){
rep(j, 1, t)
rep(i, 1, n) fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
int lca(int x, int y){
if(deep[x] < deep[y]) swap(x, y);
repd(i, t, 0)
if(deep[fa[x][i]] >= deep[y]) x = fa[x][i];
if(x == y) return x;
repd(i, t, 0)
if(fa[x][i] != fa[y][i]){
x = fa[x][i];
y = fa[y][i];
}
return fa[x][0];
}
int find(int x){
if (x == fa2[x]) return x;
return fa2[x] = find(fa2[x]);
}
int main(){
int m;
int times = 0;
while(~scanf("%d%d", &n, &m) && n && m){
printf("Case %d:\n", ++times);
init();
t = log2(n) + 1;
rep(i, 1, m){
int u, v;
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
rep(i, 1, n)
if(!dfn[i]) tarjan(1, -1);
rep(i, 1, n){
if(!c[i]){
++dcc;
dfs(i);
}
}
rep(i, 0, cnt - 1){
int u = to[i ^ 1], v = to[i];
if(c[u] == c[v]) continue;
addedge2(c[u], c[v]);
}
deep[1] = 1;
dfs2(1);
init2();
int k;
scanf("%d", &k);
int ans = dcc - 1;
rep(i, 1, dcc) fa2[i] = i;
rep(i, 1, k){
int u, v;
scanf("%d%d", &u, &v);
int x = c[u], y = c[v], father = lca(x, y);
x = find(x);
while(deep[x] > deep[father]){
fa2[x] = fa[x][0];
ans--;
x = find(x);
}
y = find(y);
while(deep[y] > deep[father]){
fa2[y] = fa[y][0];
ans--;
y = find(y);
}
printf("%d\n", ans);
}
puts("");
}
return 0;
}