http://poj.org/problem?id=3694
2008 Asia Hefei Regional Contest Online,by USTC
维护一张图中桥的个数,支持动态加边(加边次数为 q 次)。
1≤|V|≤100000,1≤|E|≤200000,1≤q≤1000
对于初始的图,用tarjan算法生成一棵DFS树以及其DFS序,并同时求出最开始时整张图中的桥的个数,将除了桥边以外的所有边 (u,v) 中的点 u与点v 在并查集中合并,这样我们就能得到初始时整张图去掉桥边之后的联通块。
注意DFS序有一个非常好的特性:对于点 x 而言,点x的DFS序标号 < 它的左子树中的所有节点的DFS序标号 <它的右子树中所有节点的 的DFS序标号,这个特性会在下面得到运用。
对于之后的每次加边 (u′,v′) 操作,有以下两种情况:
Case 1.u与v不在一条链上,设 v 在 u 的右边。让 v 先不断向上爬,直到爬到 u 与 v 的LCA即 lca(u,v) 上,其间会经过若干联通块,若经过一条边 E 链接了两个不同的联通块,则表明访问了一个桥边,标记减少了一条桥边(因为加入了边 (u,v) 后这些桥边都将消失)。 v 爬到了 lca(u,v) 后,就让 u 也爬到 lca(u,v) 上,处理方法与 v 的相同
Case 2.u与v在一条链上,不妨设 u 在 v 上面,就让 v 爬到 u 即可,处理方法与Case 1相同。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#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]<dfn[v])
{
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;i<MAXV;i++) f[i]=i;
for(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;
}