POJ 3694 Network 邊雙連通分量+LCA

~hsm~發表於2019-03-09

title

POJ 3694
CH POJ3694

analysis

求雙連通分量,利用並查集縮點,形成一棵樹,樹邊肯定都是橋,然後每對點x,y,找原圖中x,y點對應的新圖中的點,如果不是一個點,則向上找它們的LCA,因為它們之間連了一條邊,所以這些點到它們的LCA之間的邊都不是割邊了,找LCA時,先將兩點上升到同一層次,然後一起再向上找父親節點,其間遇到橋就把橋的標記刪除,並且答案減1。

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
	ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int dfn[maxn],low[maxn],id;
int fa[maxn],d[maxn],num;
bool bridge[maxn<<1];
inline void tarjan(int x)
{
	dfn[x]=low[x]=++id;
	d[x]=d[fa[x]]+1;
	for (int i=head[x];i;i=Next[i])
	{
		int y=ver[i];
		if (!dfn[y])
		{
			fa[y]=x;
			tarjan(y);
			low[x]=min(low[x],low[y]);
			if (low[y]>dfn[x])
				++num,bridge[y]=1;
		}
		else if (y!=fa[x])
			low[x]=min(low[x],dfn[y]);
	}
}
inline void lca(int x,int y)
{
	while (d[x]>d[y])
	{
		if (bridge[x])
			--num,bridge[x]=0;
		x=fa[x];
	}
	while (d[y]>d[x])
	{
		if (bridge[y])
			--num,bridge[y]=0;
		y=fa[y];
	}
	while (x!=y)
	{
		if (bridge[x]) --num,bridge[x]=0;
		if (bridge[y]) --num,bridge[y]=0;
		x=fa[x];
		y=fa[y];
	}
}
int n,m,flag;
inline void Clear()
{
	memset(d,0,sizeof(d));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(bridge,0,sizeof(bridge));
	memset(head,0,sizeof(head));
	len=id=num=0;
	for (int i=1;i<=n;++i)
		fa[i]=i;
}
int main()
{
	while (1)
	{
		read(n);read(m);
		if (!n && !m) break;
		Clear();
		printf("Case %d:\n",++flag);
		for (int i=1;i<=m;++i)
		{
			int x,y;
			read(x);read(y);
			add(x,y);add(y,x);
		}
		tarjan(1);
		int q;read(q);
		for (int i=1;i<=q;++i)
		{
			int x,y;
			read(x);read(y);
			lca(x,y);
			printf("%d\n",num);
		}
		puts("");
	}
	return 0;
}

相關文章