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; }