学习笔记(双两通分量)

双联通分量

  • 边双,对于任意两个点存在至少两条边不同的路径
  • 点双,对于任意两个点存在至少两条点不同的路径
性质
  • 显然如果是点双就一定是边双
求法
  • 边双有很好的求法,根据定义如果此边为割边(dfn[v]>low[u])(即u点儿子v无法到达u,此边为割边),那么一定不是边双,直接将割边去掉,剩下的联通快即为边双

  • 点双,同样运用到Tarjan算法,注意每次遍历一条边便将其压入栈中,如果该边为割点(low[v]>=dfn[u]),则像强连通那样一直弹边,将每条边的点标记,要注意,每个点都可能属于多个点双之中,每次覆盖成最新编号即可。一直弹到此边上一次出现即可

  • 此类问题写Tarjan时因为是双向边,所以要记一下父亲,不然要死循环在里面

一道题改了一个晚上,效率低了点。说一下个人想法吧,最重要的是要注意如果没有被遍历过就Tarjan,否则还要判断如果他的儿子的dfs序在他之前却又不是他父亲,也要将这条边压入栈中。(看不懂那些压点的)
板子 poj2942

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef int sign;
typedef long long ll;
#define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int N=1e3+5;
bool cmax(sign &a,sign b){return (a<b)?a=b,1:0;}
bool cmin(sign &a,sign b){return (a>b)?a=b,1:0;}
template<typename T>T read()
{
	T ans=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')f=-1,ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-'0'),ch=getchar();
	return ans*f;
}
void file()
{
	#ifndef ONLINE_JUDGE
		freopen("2942.in","r",stdin);
		freopen("2942.out","w",stdout);
	#endif
}
int n,m;
bool mp[N][N];
int head[N],tt;
int bl[N],cl[N],dfn[N],low[N],iscut[N],scc,id,dfs_clock;
struct edge
{
	int u,v,nex;
}e[N*N];
int l[N],q[N];
bool ok[N];
void init()
{
	tt=0;dfs_clock=0;l[0]=q[0]=0;
	memset(head,0,sizeof head);
	memset(mp,0,sizeof mp);
	memset(dfn,0,sizeof dfn);
	memset(low,0,sizeof low);
	memset(ok,0,sizeof ok);
	memset(iscut,0,sizeof iscut);
	memset(cl,0,sizeof cl);
	memset(bl,0,sizeof bl);
}
void add(int x,int y)
{
	++tt;e[tt].u=x;e[tt].v=y;e[tt].nex=head[x];head[x]=tt;
}
void input()
{
	int x,y;
	For(i,1,m)
	{
		x=read<int>();y=read<int>();
		mp[x][y]=mp[y][x]=1;
	}
}
void build()
{
	For(i,1,n)For(j,i+1,n)
	{
		if(!mp[i][j])add(i,j),add(j,i);
	}
}
bool dfs(int u)
{
//	printf("%d\n",u);
	int v;
	for(register int i=head[u];i;i=e[i].nex)
	{
		v=e[i].v;
		if(bl[v]^id)continue;
		if(cl[v]==cl[u])return 0;
		if(!cl[v])
		{
			cl[v]=3-cl[u];
			if(!dfs(v))return 0;
		}
	}
	return 1;
}
void Tarjan(int u,int fa)
{
	int v,x,y,ch=0;
	low[u]=dfn[u]=++dfs_clock;
	for(register int i=head[u];i;i=e[i].nex)
	{
		v=e[i].v;
		if(fa==v)continue;
		if(!dfn[v])
		{
			l[++l[0]]=i;ch++;
			Tarjan(v,u);
			cmin(low[u],low[v]);
			if(low[v]>=dfn[u])
			{
				iscut[u]=1;id++;
				while(1)
				{
					x=e[l[l[0]]].u;y=e[l[l[0]--]].v;
					//cerr<<x<<' '<<y<<endl;
					if(bl[x]^id)bl[x]=id;
					if(bl[y]^id)bl[y]=id;
					if(x==u&&v==y)break;
				}
				cl[u]=1;
				if(!dfs(u))
				{
					For(i,1,n)if(bl[i]==id)ok[i]=1;
				}
				cl[u]=0;q[0]=0;
			}
		}
		else if(dfn[v]<dfn[u])
		{
			l[++l[0]]=i;//cerr<<1<<endl;
			cmin(low[u],dfn[v]);
		}
	}
	if(ch==1&&!fa)iscut[u]=0;
}
void work()
{
	For(i,1,n)
	{
		if(!dfn[i])Tarjan(i,0);
	}
	int cnt=0;
	For(i,1,n)if(!ok[i])++cnt;
	printf("%d\n",cnt);
}
int main()
{
	file();
	while(scanf("%d%d",&n,&m))
	{
		if(!n&&!m)break;
		init();
		input();
		build();
		work();
	}
	return 0;
}
posted @ 2018-01-07 16:46  dyx_diversion  阅读(149)  评论(0编辑  收藏  举报