http://poj.org/problem?id=3694
题意:给定一个有N个点,M条边的无向图(图原本连通),N<=10000 ,M<=20000,现在要求将Q条边逐一加入到无向图中去,要求对于每次加入边之后,输出图中的桥的条数。
思路:先对原无向图缩点,初始时候,桥的条数为:cnt -1 , 其中cnt为图中连通分量的个数,每次增加一条边的时候,我们可以先找出两个顶点的LCA,然后沿着树边往根移动,将这个圈又进行缩点,此时就可以在O(N)的时候内求出此时的桥。因为只需要求两个点之间的LCA,所有可以直接从两个结点往上找,知道找到相同的结点为止。
代码:
#include<stdio.h>
#include<string.h>
const int MAXN = 100010 ;
const int MAXM = 200010 ;
int N , M , Q ;
int Gnext[MAXM*2] ;
int Gv[MAXM*2] ;
int Gr[MAXN], Gc ;
int gnext[MAXM*2] ;
int gv[MAXM*2] ;
int gr[MAXN],gc ;
int ans ;
void init(){
memset(Gr, -1 ,sizeof(Gr));
memset(gr, -1, sizeof(gr)) ;
Gc = 0 ; gc = 0 ;
}
void add(int a,int b){
Gv[Gc] = b ;
Gnext[Gc] = Gr[a] ;
Gr[a] = Gc++ ;
}
int dfn[MAXN] , low[MAXN] , col[MAXN] ,st[MAXN];
int idx, top, cnt ;
void tarjin(int x,int fa){
low[x] = dfn[x] = ++idx ;
st[top++] = x ;
bool f = 1 ;
for(int i=Gr[x] ;i!=-1;i=Gnext[i]){
int v = Gv[i] ;
if(v==fa && f){
f = 0 ; continue ;
}
if( dfn[v] == -1){
tarjin(v,x);
if( low[v] < low[x]) low[x] = low[v] ;
if( low[v] > dfn[x] ){
for( st[top]=-1 ;st[top]!=v;){
top-- ;
col[ st[top] ] = cnt ;
}
cnt++ ;
}
}
else if(dfn[v] < low[x])
low[x] = dfn[v] ;
}
}
void add1(int a, int b){
gv[gc] = b ;
gnext[gc] = gr[a] ;
gr[a] = gc++ ;
}
int p[MAXN] ;
void solve(){
top = idx = 0 ;
cnt = 1 ;
memset(dfn, -1, sizeof(dfn)) ;
memset(col,0, sizeof(col)) ;
for(int i=1;i<=N;i++){
if( dfn[i] == -1){
tarjin(i,-1);
}
}
for(int i=1;i<=N;i++){
for(int j=Gr[i] ;j!=-1;j=Gnext[j]){
int u = Gv[j] ;
if( col[i] != col[u] ){
add1( col[i] , col[u] );
}
}
}
for(int i=0;i<cnt;i++){
p[i] = i ;
}
}
int pre[MAXN] ;
bool vis[MAXN] ;
void build(int u,int fa){
for(int i=gr[u];i!=-1;i=gnext[i]){
int v = gv[i] ;
if(v == fa) continue ;
pre[v] = u ;
build(v,u) ;
}
}
void deal(int a, int b){
memset(vis, 0 ,sizeof(vis));
int aa = a ;
vis[aa] = 1 ;
int ac ,bc ;
ac = bc = 0 ;
while(pre[aa] != -1){
vis[ pre[aa] ] = 1 ;
aa = pre[aa] ;
}
int bb = b ;
int ancer = -1;
if( vis[bb] == 1) ancer = bb ;
else{
while( pre[bb]!=-1 && !vis[ pre[bb] ]){
bb = pre[bb] ;
}
if( pre[bb]!=-1 ){
ancer = pre[bb] ;
}
}
if(ancer == -1) return ;
aa = a ;
while( aa != ancer){
ac ++ ;
aa = pre[aa] ;
}
bb = b ;
while( bb != ancer){
bc ++ ;
bb = pre[bb] ;
}
ans = ans - ac - bc ;
for(aa=a;aa!=ancer;aa=pre[aa]){
p[aa] = ancer ;
for(int j=gr[aa] ;j!=-1;j=gnext[j]){
int v = gv[j] ;
if(v == pre[aa]) continue ;
pre[v] = ancer ;
}
}
for(bb=b;bb!=ancer;bb=pre[bb]){
p[bb] = ancer ;
for(int j=gr[bb] ;j!=-1;j=gnext[j]){
int v = gv[j] ;
if(v == pre[bb]) continue ;
pre[v] = ancer ;
}
}
}
int find(int a){
if(a != p[a]){
p[a] = find( p[a] ) ;
}
return p[a] ;
}
int main(){
int a, b ;
int cas = 0 ;
while(scanf("%d%d",&N,&M) == 2){
if(0==N && M==0) break ;
++cas ;
init() ;
for(int i=1;i<=M;i++){
scanf("%d%d",&a,&b);
add(a,b); add(b,a);
}
solve();
memset(pre, -1, sizeof(pre));
build(0,-1);
scanf("%d",&Q);
ans = cnt - 1;
printf("Case %d:\n",cas);
for(int i=1;i<=Q;i++){
scanf("%d%d",&a,&b);
int fa = find( col[a] ) ;
int fb = find( col[b] ) ;
if( fa == fb ){
printf("%d\n",ans);
}
else{
deal( fa,fb );
printf("%d\n",ans);
}
}
printf("\n");
}
return 0;
}