POJ_3694 Network Tarjin + LCA + 并查集

来源:互联网 发布:mac软件无法强制退出 编辑:程序博客网 时间:2024/04/29 22:59

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



原创粉丝点击