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

 

posted @ 2018-08-16 15:05  xiuwenL  阅读(159)  评论(0编辑  收藏  举报