在无向图求完tarjan 并且缩点后 剩下的是树 树上的边全是 割边 添加 x,y联通块 之间的树边全变为普通边 所以我们只需要 维护联通块 即可 可以使用并查集 复杂度约为 mq 此题我们可以直接考虑 用 on的方法来找lca 复杂度为 nm 考虑一下正确性
考虑一下此图 点2到点5与点4到点7是否会错过 因为 倘若先走2 - 5那么6 就会成为7的父节点 而不是 4 若先走2 - 3那么 也是正确 所以on找 父节点是可以的
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 1e5 + 10,M = 2e5 + 10;
int n,m;
int head[N],to[M * 2],last[M * 2],cnt;
void add(int a,int b){
to[++cnt] = b;
last[cnt] = head[a];
head[a] = cnt;
}
int dfn[N],low[N],times,top,pre[N],bridge[N],sum;
void tarjan(int x,int lastt){
dfn[x] = low[x] = ++times;
for(int i = head[x]; i != -1; i = last[i]){
int j = to[i];
if(!dfn[j]){
tarjan(j,i);
low[x] = min(low[x],low[j]);
pre[j] = x;
if(dfn[x] < low[j]){
bridge[j] = 1;
sum++;
}
}else if(i != (lastt ^ 1)){
low[x] = min(low[x],dfn[j]);
}
}
}
int judge(int x,int y){
int ans = 0;
while(dfn[x] > dfn[y]){
if(bridge[x]){
ans++;
bridge[x] = 0;
}
x = pre[x];
}
while(dfn[x] < dfn[y]){
if(bridge[y]){
ans++;
bridge[y] = 0;
}
y = pre[y];
}
while(x != y){
if(bridge[x]){
bridge[x] = 0;
ans++;
}
if(bridge[y]){
bridge[y] = 0;
ans++;
}
x = pre[x];
y = pre[y];
}
return ans;
}
int main(){
int CASE = 0;
while(~scanf("%d%d",&n,&m)){
if(n == 0 && m == 0) break;
cnt = 1;
sum = 0;
memset(head,-1,sizeof head);
memset(dfn,0,sizeof dfn);
memset(low,0,sizeof low);
memset(bridge,0,sizeof bridge);
for(int i = 1; i <= m; i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for(int i = 1; i <= n; i++){
if(!dfn[i]){
tarjan(i,0);
}
}
printf("Case %d:\n",++CASE);
int q;
scanf("%d",&q);
while(q--){
int x,y;
scanf("%d%d",&x,&y);
cout << (sum -= judge(x,y)) << endl;
}
}
return 0;
}