[POJ 3694]Network(Tarjan+并查集+LCA 动态加边求图中桥的个数)

题目链接

http://poj.org/problem?id=3694

题目来源

2008 Asia Hefei Regional Contest Online,by USTC

题目大意

维护一张图中桥的个数,支持动态加边(加边次数为q次)。
1|V|100000,1|E|200000,1q1000

思路

对于初始的图,用tarjan算法生成一棵DFS树以及其DFS序,并同时求出最开始时整张图中的桥的个数,将除了桥边以外的所有边(u,v)中的点uv在并查集中合并,这样我们就能得到初始时整张图去掉桥边之后的联通块。
注意DFS序有一个非常好的特性:对于点x而言,点x的DFS序标号<它的左子树中的所有节点的DFS序标号<的DFS序标号,这个特性会在下面得到运用。
对于之后的每次加边(u,v)操作,有以下两种情况:
[POJ 3694]Network(Tarjan+并查集+LCA 动态加边求图中桥的个数)_第1张图片
Case 1.u与v不在一条链上,设vu的右边。让v先不断向上爬,直到爬到uv的LCA即lca(u,v)上,其间会经过若干联通块,若经过一条边E链接了两个不同的联通块,则表明访问了一个桥边,标记减少了一条桥边(因为加入了边(u,v)后这些桥边都将消失)。v爬到了lca(u,v)后,就让u也爬到lca(u,v)上,处理方法与v的相同
[POJ 3694]Network(Tarjan+并查集+LCA 动态加边求图中桥的个数)_第2张图片
Case 2.u与v在一条链上,不妨设uv上面,就让v爬到u即可,处理方法与Case 1相同。

代码

#include 
#include 
#include 
#include 

#define MAXV 110000
#define MAXE 410000

using namespace std;

struct edge
{
    int u,v,next;
}edges[MAXE];

int head[MAXV],nCount=0;

void AddEdge(int U,int V)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

int f[MAXV];

int findSet(int x)
{
    if(f[x]==x) return x;
    return f[x]=findSet(f[x]);
}

int dfn[MAXV],low[MAXV],idx=0;
int father[MAXV];
int bridgeNum=0;

bool Union(int u,int v)
{
    int rootu=findSet(u),rootv=findSet(v);
    if(rootu==rootv) return false;
    f[rootu]=rootv;
    return true;
}

void tarjan(int u,int fa)
{
    dfn[u]=low[u]=++idx;
    for(int p=head[u];p!=-1;p=edges[p].next)
    {
        int v=edges[p].v;
        if(v==fa) continue;
        if(!dfn[v])
        {
            father[v]=u;
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u]) bridgeNum++;
            else
                Union(u,v);
        }
        else
            low[u]=min(low[u],dfn[v]);
    }
}

int solve(int u,int v) //加入边u-v
{
    if(findSet(u)==findSet(v)) return bridgeNum; //加入边u-v之前u和v是联通的
    if(dfn[u]>dfn[v]) swap(u,v); //保证dfs序中u在v左边
    while(dfn[u]if(Union(father[v],v))
            bridgeNum--;
        v=father[v];
    }
    while(u!=v)
    {
        if(Union(u,father[u]))
            bridgeNum--;
        u=father[u];
    }
    return bridgeNum;
}

int main()
{
    int n,m,T=0;
    while(scanf("%d%d",&n,&m)!=EOF&&!(!n&&!m))
    {
        printf("Case %d:\n",++T);
        nCount=0;
        memset(head,-1,sizeof(head));
        memset(father,0,sizeof(father));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        for(int i=0;ifor(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            AddEdge(u,v);
            AddEdge(v,u);
        }
        father[1]=1;
        tarjan(1,1);
        int q;
        scanf("%d",&q);
        while(q--)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            printf("%d\n",solve(u,v));
        }
    }
    return 0;
}

你可能感兴趣的:(ACM-ICPC,图论,传统题,动态图问题(Link-Cut,Tree等),POJ)