2022.02.25 Tarjan

https://www.cnblogs.com/c1299401227/p/5402747.html

1. 求有向图强连通分量

1.1 为什么不是无向图

毕竟无向图中,一个边过去,还可以从这个边回来。

1.2 代码实现

inline void Tarjan(int x){
	low[x]=dfn[x]=++ind;
	stacki.push(x);instacki[x]=1;
	for(int i=head[x];i;i=a[i].next){
		int v=a[i].to;
		if(!dfn[v])Tarjan(v),low[x]=min(low[x],low[v]);
		else if(instacki[x])low[x]=min(low[x],low[v]);
	}
	int k=0;
	if(low[x]==dfn[x]){
		++tot;
		do{
			k=stacki.top();stacki.pop();
			instacki[k]=0;
			belong[k]=tot;
		}while(k!=x);
	}
}

2.求无向图割点

2.1 定义

割点:原来联通的图,删除某点后,图不连通。

2.2 思路

如果是根节点,那么它必须有两棵子树,否则,只有一棵子树,删了之后,还是联通的;

对于其他节点,它的子节点向上走,最多只能走到它自己,再向上就翻不上去。如果能翻到它上面,说明有环了,去掉它,,依旧联通,即\(low[v]>=dfn[x]\)

2.3 代码实现

注意:因为是无向图,它不能再回到它爹那里去。

inline void Tarjan(int x,int fai){
	dfn[x]=low[x]=++ind;
	stakci.push(x);instacki[x]=1;
	int son=0;
	for(int i=head[x];i;i=a[i].next){
		int v=a[i].to;
		if(!dfn[v]){
			Tarjan(v,x);
			low[x]=min(low[x],low[v]);
			if(low[v]>=dfn[x])cutstain[x]=1;
			if(x==root)++son;
		}else if(fai!=v)low[x]=min(low[x],dfn[v]);
	}
	if(x==root&&son==1)cutstain[x]=0;//x is root,but it only has one son.
}

3. 求无向图桥

3.1 定义

桥:原理来的图联通,删除一条边后,新图不连通。

3.2 思路

只有\(low[v]>>dfn[x]\)时,这一条从x到v的边才是桥。这是因为,如果是一个桥,它的儿子不能翻到它那里。

但是要注意:小心重边!!而且,从v到x可不是一个环,这只是一条无向边!

3.3 代码实现

inline void Trajan(int x,int fai){
	dfn[x]=low[x]++ind;
	stacki.push(x);instacki[x]=1;
	for(int i=head[x];i;i=a[i].next){
		int v=a[i].to;
		if(!dfn[v]){
			Tarjan(v,x);
			low[x]=min(low[x],low[v]);
			if(low[v]>dfn[x])cutedge[i]=1;
		}else if(v!=fai)low[x]=min(low[x],dfn[v]);
	}
}

4. 求无向图的边双

4.1 定义

如果一个无向连通图中,没有割边,那么这个无向连通图就是一个边双连通图。

一个割点可以属于多个点双。但是一条割边不属于任何边双。

我认为就是一个去了桥的图,原图连通不了了,可不就边双了。原图中有边双,那就说明新图的两部分想要到达彼此只有一条路线,那么,原图不是个好图,因为它不是个边双 /斜眼笑。

如果任意两点至少存在两条边不重复路径,则称该图为边双连通的。

所以,对于一个边双连通分量,里面任意两个点都至少存在两个边不重复的路径。

4.2 思路

首先,求出桥;

其次,dfs对于剩下的边染色;

最后,结束。

4.3 代码实现

inline void color(int x,int col){
	Color[x]=col;
	for(int i=head[x];i;i=a[i].next){
		int v=a[i].to;
		if(cutedge[i])continue;
		if(!Color[v])color(v,col);
		//没有判断v是不是fa,只是因为 ,fa在x之前已经被染过色了 
	}
}
inline void ebcc(){
	for(int i=1;i<=n;i++)if(!Color[i])
	++tot,color(i,tot);
}

5. 求无向图点双

5.1 定义

https://blog.csdn.net/qq_39670434/article/details/81973923

在一个无向连通图中,如果没有割点,那么它是点双连通图。
如果只有两个点,那么也是点双。

一个割点可以属于多个点双。但是一条割边不属于任何边双。

https://blog.csdn.net/ACTerminate/article/details/52526920?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-2.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-2.pc_relevant_default

对于任意一个点双连通分量,删除任意一点,连通性不变,但不一定是点双哟,孩纸。

5.2 思路

首先,找到那些割点,然后把它们所在的边扔进stack里;

然后,把剩下的图染色;

最后,结束。

5.3 代码实现

https://blog.csdn.net/fuyukai/article/details/51303292

这个代码肯定来自刘汝佳的蓝书,因为我当时也抄过,望天~

inline void Tarjan(int x,int fai){
	dfn[x]=low[x]=++ind;
	int son=0;
	for(int i=head[x];i;i=a[i].next){
		int v=a[i].to;
		if(!dfn[v]){
			stacki.push(i);
			Tarjan(v,x);
			low[x]=min(low[x],low[v]);
			if(low[v]>=dfn[x]){
				cutstain[x]=1;
				++tot;sizei=0;//tot: the num of vbcc sizei: the num of this vbcc
				vbcc[tot].clear();//vbcc: the vector of this bcc
				while(1){
					int k=stacki.top();stacki.pop();
					if(belong[a[k].from]!=tot){
						vbcc[tot].push_back(a[k].from);
						belong[a[k].from]=tot;
					}
					if(belong[a[k].to]!=tot){
						vbcc[tot].push_back(a[k].to);
						belong[a[k].to]=tot;
					}
					if(a[k].from==x&&a[k].to==v)break;
				}
			}
			if(x==root)++son;
		}else if(fai!=v)
		stacki.push(i),
		low[x]=min(low[x],dfn[v]);
	}
	if(x==root&&son==1)cutstain[x]=0;//x is root,but it only has one son.
}
inline void  vbcc(){
	for(int i=1;i<=n;i++)if(!dfn[i])Tarjan(x,0);
}
 posted on 2022-04-15 20:50  eleveni  阅读(52)  评论(0)    收藏  举报