简单题意
给出一个无向图,然后给出一些点对,在点对之间连边,问每次连边之后图中还有多少桥。
思路
先用Tarjan算法求桥,同时用并查集缩点,把所有非桥的边缩起来,这样剩下的就是一个只包含桥的树,在每次加边的时候通过并查集,判断两点是否在缩起来的点中,如果是则加入该边对桥的数量没有任何影响直接返回,如果不是就肯定会构成环,这是要找两者的最近公共祖先,可以通过之前的Tarjan算法遗留的dfn信息来做,两点到LCA的沿途的所有边都是新环上的边,都缩起来为下一次询问做准备,同时减去相应的桥的数量。
代码
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int maxn = 1e5+5;
vector <int> G[maxn];
int dfn[maxn],low[maxn];
int fa[maxn];
int pre[maxn];
int bricnt = 0,times = 0;
void init(int n){
for(int i = 0 ; i <= n ; i ++){
G[i].clear();
fa[i] = i;
pre[i] = i;
}
bricnt = times = 0;
memset(dfn,-1,sizeof dfn);
memset(low,-1,sizeof low);
}
inline void addedge(int from, int to){
G[from].push_back(to);
G[to].push_back(from);
}
int Find(int x){
return fa[x] == x ? fa[x]:fa[x] = Find(fa[x]);
}
bool Union(int x, int y){
int a = Find(x);
int b = Find(y);
if(a==b) return false;
fa[b] = a;
return true;
}
inline int del(int x){
if(Union(pre[x],x)) bricnt --;
return pre[x];
}
int Lca(int u,int v){
if(Find(u) == Find(v))
return bricnt;
if(dfn[u] > dfn[v])
swap(u,v);
while(dfn[u] < dfn[v])
v = del(v);
while(u!=v){
u = del(u);
}
}
void Tarjan(int cur,int from){
bool flag = true;
dfn[cur] = low[cur] = times++;
for(int i = 0 ; i < G[cur].size(); i ++){
int to = G[cur][i];
if(from == to && flag){//flag处理重边
flag = 0;
continue;
}
if(dfn[to] == -1){
pre[to] = cur;
Tarjan(to,cur);
low[cur] = min(low[cur],low[to]);
if(low[to] > dfn[cur])
bricnt++;
else
Union(cur,to);
}
else
low[cur] = min(low[cur], dfn[to]);
}
}
int main(){
int n,m,Q;
int kas = 1;
while(~scanf("%d %d", &n,&m),(n+m)){
init(n);
for(int i = 0 ; i < m ; i ++){
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v);
}
Tarjan(1,0);
scanf("%d", &Q);
printf("Case %d:\n",kas++);
for(int i = 0 ; i < Q; i ++){
int u,v;
scanf("%d %d",&u,&v);
Lca(u,v);
printf("%d\n",bricnt);
}
puts("");
}
return 0;
}