好题。边双联通分量。
题目给了一个连通的无向图。那么,双连通分量缩点后,就得到一棵树。
每个操作就是将树上两个点和一直到公共祖先的所有点缩成一个点。
题目给了一个连通的无向图。那么,双连通分量缩点后,就得到一棵树。
每个操作就是将树上两个点和一直到公共祖先的所有点缩成一个点。
用并查集可以巧妙地完成这个操作。每次找到两个点的最近公共祖先,将两点到该祖先路径上的点都合并到祖先的并查集里,并进行路径压缩。路径上的边有多少,就将桥的数目减少多少。很方便就统计出剩下的桥的数目了。
算法每条边最多遍历一次,这样,最差的复杂度就是O(m)。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
#define MM 411100
#define NN 202000
int n,m,i,a,b,q,set[NN],now,cas,tote,first[NN],next[MM],v[MM],fi[NN],fv[2*MM],fn[2*MM],fe;
int totb,totdfn,top,low[NN],dfn[NN],sta[NN],block[NN];
int vis[NN],deg[NN],fa[NN];
void init(){memset(first,-1,sizeof(first));tote=0;}
inline void addedge(int a,int b){
tote++;next[tote]=first[a];first[a]=tote;v[tote]=b;
tote++;next[tote]=first[b];first[b]=tote;v[tote]=a;
}
void tarjan(int u,int fa){
dfn[u]=low[u]=++totdfn;
sta[++top]=u;
int e,tmp;
for(e=first[u];e!=-1;e=next[e])if (v[e]!=fa){
if (!dfn[v[e]]){
tarjan(v[e],u);
if (low[v[e]]<low[u]) low[u]=low[v[e]];
if (dfn[u]<low[v[e]]) {
++totb;
do{
tmp=sta[top--];
block[tmp]=totb;
}while(tmp!=v[e]);
}
}
else if (dfn[v[e]]<low[u]) low[u]=dfn[v[e]];
}
}
void bcc(){
totb=totdfn=top=0;
memset(dfn,0,sizeof(dfn));
tarjan(1,-1);
int tmp;
if (top>0){
++totb;
while(1){
if (top==0) break;
tmp=sta[top--];
block[tmp]=totb;
}
}
}
inline void addedge2(int a,int b){
fe++;fn[fe]=fi[a];fi[a]=fe;fv[fe]=b;
fe++;fn[fe]=fi[b];fi[b]=fe;fv[fe]=a;
}
void dfs(int u,int f,int d){
fa[u]=f;
deg[u]=d;
int e;
vis[u]=1;
for(e=fi[u];e!=-1;e=fn[e])if (!vis[fv[e]]){
dfs(fv[e],u,d+1);
}
}
void rebuild_graph(){
memset(fi,-1,sizeof(fi));
fe=0;
int u,i,e;
for(u=1;u<=n;u++){
for(e=first[u];e!=-1;e=next[e]){
addedge2(block[u],block[v[e]]);
}
}
memset(vis,0,sizeof(vis));
dfs(1,-1,1);
}
inline int find(int x){return x==set[x]?x:set[x]=find(set[x]);}
void work(int a,int b){
a=block[a];b=block[b];
int lca,ta=find(a),tb=find(b),cnt;
while(ta!=tb){
if (deg[ta]>deg[tb]) ta=fa[ta];
else if (deg[tb]>deg[ta]) tb=fa[tb];
else {ta=fa[ta];tb=fa[tb];}
ta=find(ta);tb=find(tb);
}
lca=ta;
ta=find(a);tb=find(b);
cnt=0;
while(ta!=lca){
set[ta]=lca;
ta=fa[ta];
ta=find(ta);
cnt++;
}
while(tb!=lca){
set[tb]=lca;
tb=fa[tb];
tb=find(tb);
cnt++;
}
now-=cnt;
}
struct Edge{int a,b;}edge[MM];
int eq(Edge x,Edge y){if (x.a!=y.a||x.b!=y.b) return 0;return 1;}
inline bool cmp(Edge x,Edge y){return x.a==y.a?x.b<y.b:x.a<y.a;}
int main(){
//freopen("3694in.txt","r",stdin);
cas=0;
while(scanf("%d%d",&n,&m)&&(n||m)){
init();
edge[0].a=edge[0].b=0;
for(i=1;i<=m;i++){
scanf("%d%d",&a,&b);
edge[i].a=min(a,b);edge[i].b=max(a,b);
}
sort(edge+1,edge+m+1,cmp);
for(i=1;i<=m;i++)
if (eq(edge[i],edge[i-1])==0) addedge(edge[i].a,edge[i].b);
bcc();
rebuild_graph();
for(i=1;i<=n;i++){set[i]=i;}
now=totb-1;
for(i=1;i<=m;i++) if (eq(edge[i],edge[i-1])) work(edge[i].a,edge[i].b);
scanf("%d",&q);
printf("Case %d:\n",++cas);
for(i=1;i<=q;i++){
scanf("%d%d",&a,&b);
work(a,b);
printf("%d\n",now);
}
printf("\n");
}
return 0;
}