POJ - 3694 Network (割邊+LCA)


題意:給一張無向圖,有M次加邊的操作,每次操作之后輸出割邊的數目。

分析:顯然,割邊肯定出現在任意一棵生成樹中,用數組f[u]記錄點u在dfs樹上的父親節點,用這種方式就可以快速地找出dfs樹上的任意一條邊。在u,v之間加邊后,原來的減去的割邊肯定是u,v在dfs樹上的最短路徑中出現。那么每次操作之后,在求u,v兩點lca的過程中,就可以判斷減少了多少條割邊。

但O(n)地求lca效率上並不好,仍有改進的空間。在求lca的過程中,如果一條邊確定不是割邊,那么之后也沒有訪問它的必要,所以可以將其向上合並。

並且在初始Tarjan求割邊的過程中,就可將不是割邊的邊向上合並。

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<cstring>
using namespace std;
const int maxn =1e5+5;
struct Edge{
    int to,next;
}edges[maxn*4];
int pre[maxn],low[maxn],f[maxn],head[maxn],dfn,tot,top;
bool judge[maxn];
void init()
{
    dfn=tot=top=0;
    f[1]=1;
    memset(pre,0,sizeof(pre));
    memset(low,0,sizeof(low));
    memset(head,-1,sizeof(head));
    memset(judge,0,sizeof(judge));
}

int Find(int x){ return f[x]==x? x:f[x] =Find(f[x]); }

void Union(int u,int v)
{
    int fu= Find(u);
    int fv = Find(v);
    if(fu!=fv){             //后向前並
        f[fv] =fu;
    }
}

void AddEdge(int u,int v)
{
    edges[top].to =v;
    edges[top].next = head[u];
    head[u] = top++;
}

void Tarjan(int u,int fa,int id)            //id表示邊的序號
{
    f[u]=fa;
    pre[u] = low[u]= ++dfn;
    int v;
    for(int i=head[u];i!=-1;i=edges[i].next){
        v = edges[i].to;
        if(i==(id^1))   continue;   //實際上是一條邊 沒有訪問的必要
        if(!pre[v]){
            Tarjan(v,u,i);
            low[u] = min(low[u],low[v]);
        }
        else
            low[u]=min(low[u],pre[v]);
        if(low[v]>pre[u]){              //割邊
            tot++;
            judge[v]=true;
        }
        else
            Union(u,v);             //縮點
    }   
}

void LCA(int u,int v)
{
    if(pre[v]<pre[u])
        swap(u,v);
    while(pre[v]>pre[u]){
        if(judge[v]){tot--;judge[v]=false;}
        v = f[v];
    }
    while(u!=v){
        if(judge[u]){tot--;judge[u]=false;}
        if(judge[v]){tot--;judge[v]=false;}
        u = f[u];
        v = f[v];
    }
}

int main()
{
    int N,M,u,v,tmp,Q;
    int T=1;
    while(~scanf("%d%d",&N,&M),N+M){
        init();
        for(int i=0;i<M;++i){
            scanf("%d%d",&u,&v);
            AddEdge(u,v);
            AddEdge(v,u);
        }
        Tarjan(1,1,-1);
        printf("Case %d:\n",T++);
        scanf("%d",&Q);
        for(int i=0;i<Q;++i){
            scanf("%d%d",&u,&v);        //選取low較小的點作為終點
            LCA(u,v);
            printf("%d\n",tot);
        }
        printf("\n");
    }   
    return 0;
}

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: