POJ - 3694 Network(无向图+多重边+动态加边+边双连通分量+并查集+LCA)

链接:https://cn.vjudge.net/problem/POJ-3694

题意:每加一次边输出当前桥的个数。

思路:先将原图边双连通分量求出(顺便求出桥(割边)的个数),并且将边双联通分量缩点。缩点之后重新建图,肯定是树或森林,当加一点边时,如果这条边的两个点(假设为u、v)已经在同一个边双里,桥的个数不会变;否则,u、v和LCA(u,v)之间的所有点,都会在同一个边双里,减少的桥就是u->LCA(u,v)和v->LCA(u,v)的边的个数之和。这里可以用并查集维护,这样可以快速的找LCA。

#include 
#include 
#include 
#include 
#define ll long long
using namespace std;
const int N = 1e5+10;
const int M = 5e5+10;
struct node
{
	int to,nxt,id;
}g[M],g1[M];
int head[N],cnt,head1[N],cnt1;
int dfn[N],id,low[N],color[N],cl,sta[N],top;
int f[N],fa[N],deep[N];
int ans;
bool vis[N];
int n,m,q;
void Init()
{
	top=cl=id=cnt=cnt1=ans=0;
	for(int i=1;i<=n;i++)
		head[i]=head1[i]=-1,deep[i]=dfn[i]=vis[i]=0;
}
inline void add(int u,int v,int num)
{
	g[cnt].to=v; g[cnt].nxt=head[u]; g[cnt].id=num; head[u]=cnt++;
	return ;
}
inline void add1(int u,int v)
{
	g1[cnt1].to=v; g1[cnt1].nxt=head1[u]; head1[u]=cnt1++;
	return ;
}
inline void tarjan(int u,int x)
{
	int v;
	dfn[u]=low[u]=++id; sta[++top]=u; vis[u]=1; 
	for(int i=head[u];i!=-1;i=g[i].nxt)
	{
		v=g[i].to;
		//求边双时,从父亲来的边不能再走,但此题有多重边,所以要判断边的编号 
		if(g[i].id==x) continue;
		if(!dfn[v])
		{
			tarjan(v,g[i].id);
			low[u]=min(low[u],low[v]);				
		}
		else if(vis[v])
			low[u]=min(low[u],dfn[v]);	
	}
	if(dfn[u]==low[u])
	{
		//边双的个数 
		color[u]=++cl;
		while(sta[top]!=u)
		{
			color[sta[top]]=cl; vis[sta[top--]]=0;
		}
		vis[sta[top--]]=0;
	}
	return ;
}
inline void build()
{
	for(int u=1;u<=n;u++)
		for(int i=head[u];i!=-1;i=g[i].nxt)
			if(color[u]!=color[g[i].to]) add1(color[u],color[g[i].to]);
	return ;				
}
inline void dfs(int u)
{
	int v; 
	for(int i=head1[u];i!=-1;i=g1[i].nxt)
	{
		v=g1[i].to;
		if(deep[v]) continue;
		deep[v]=deep[u]+1;
		fa[v]=u;
		dfs(v);
	}
	return ;
}
inline int getf(int x)
{
	return f[x]==x?x:f[x]=getf(f[x]);
}
inline int LCA(int u,int v)
{
	while(u!=v)
	{
		if(deep[u]deep[v])
			u=fa[u];
		else u=fa[u],v=fa[v];
		u=getf(u); v=getf(v);
	}
	return u;
}
int main(void)
{
	int u,v,x,lca,tt=0;
	while(~scanf("%d%d",&n,&m)&&(n||m))
	{
		Init();	
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			add(u,v,i); add(v,u,i);
		}
		for(int i=1;i<=n;i++)
			if(!dfn[i]) tarjan(i,0);
		//tarjan(1,0);
		build();
		
		for(int i=1;i<=cl;i++)
			if(!deep[i]) deep[i]=1,fa[i]=0,dfs(i);
 		//deep[1]=1; fa[1]=0;
		//dfs(1);
		scanf("%d",&q);
		printf("Case %d:\n",++tt);
		
		ans=cl-1;
		for(int i=1;i<=cl;i++) f[i]=i;
		while(q--)
		{
			scanf("%d%d",&u,&v);
			u=color[u]; v=color[v];
			if(u==v)
			{
				printf("%d\n",ans); 
				continue;
			}
			u=getf(u); v=getf(v);
			x=0;
			lca=LCA(u,v);
			while(u!=lca)
			{
				x++;
				f[u]=lca;
				u=fa[u];
				u=getf(u);
			}
			while(v!=lca)
			{
				x++;
				f[v]=lca;
				v=fa[v];
				v=getf(v);
			}
			ans-=x;
			printf("%d\n",ans);
		}
		printf("\n");
	}
	return 0;
}

 

你可能感兴趣的:(=====图论=====,连通图)