浅谈Tarjan(双联通分量)

Tarjan-点双连通分量和边双连通分量


点双联通分量

若一个连通分量任意两点至少存在两条路径经过的点无重复(即除起终点外完全不同),则称为点双连通分量DCC)(可理解为无重复点,两条路)。

定义无向图的割点为:如果去掉无向连通图 G \mathcal G G 中的一个点 u \mathcal u u 和所有和 u u u 相邻的边 ,剩下的子图 G ′ \mathcal G' G 不再是连通图,则称 w \mathcal w w 为一个割点

因此,点双联通分量又可以简单地认为是:没有割点的连通分量

那么找点双连通分量,找割点就得了。

割点的判定法则

T a r j a n \tt Tarjan Tarjan,每次访问到一个节点就将其入栈。若有已入栈的点,重访问到了节点 t o to to,若 l o w [ t o ] > = d f n [ x ] low[to]>=dfn[x] low[to]>=dfn[x],则到 t o to to 的路径上全部出栈,将这批节点存入一个 D C C \mathcal DCC DCC

感性地理解为:这个 x x x 点成为了一个断点,稍微推下就OK了。

Code:

缩点:


缩边:

void Tarjan(LL x,LL Pre)
{
	Dfn[x] = Low[x] = ++ Cnt;
	int l = G[x].size();
	for (Int i = 0; i < l; ++ i)
	{
		LL to = G[x][i].v;
		if (to == Pre)
			continue;
		if (! Dfn[to])
		{
			S.push( to ); S.push( x ); S.push( G[x][i].Index );  
			Tarjan(to, x);
			Low[x] = Min(Low[x], Low[to]);
			if (Low[to] >= Dfn[x]) // x 是割点 
			{
				 Col ++;
				 LL u, v;
				 do
				 {
				 	Bian[Col].insert( S.top() ); S.pop();
				 	u = S.top(); S.pop(); v = S.top(); S.pop();
				 	Dian[Col].insert( u ); Dian[Col].insert( v );
				 } while (u != x || v != to);
			}
		}
		else if (Dfn[to] < Dfn[x])
		{
			S.push( to ); S.push( x ); S.push( G[x][i].Index );
			Low[x] = Min(Low[x], Dfn[to]);  
		}
	}
}

边双连通分量

若一个连通分量任意两点至少存在两条路径经过的边无重复(即完全不同),则称为边双连通分量(可理解为无重复边,两条路)。

定义无向图的割边为:如果去掉无向连通图 G \mathcal G G 中的一条边 w \mathcal w w ,剩下的子图 G ′ \mathcal G' G 不再是连通图,则称 w \mathcal w w 为一条割边,又称为

边双联通分量更简洁的表达方式为:内部无桥的连通分量。

那么对于判定边双连通分量,似乎只需要找桥就可以了。那么,如何进行桥的判定呢?

无向图的桥的判定法则

在无向图中,若边 e \mathcal e e (两端点为 u u u v v v)为桥,只需要满足 d f n [ u ] < l o w [ v ] dfn[u]<low[v] dfn[u]<low[v]

我感性的理解是:若满足这个条件,在搜索树中, u u u 能走到 v v v,而 v v v 无法从下方搜索树到 u u u,那么形成了一个“断层”,即找到了这条边为桥。

那么如何进行边双连通分量的缩点呢?其实很简单,只需要将强连通分量的算法稍微改改——判一下不走返祖边就行了。

Code:

判定桥:(最好用前向星, v e c t o r \tt vector vector 不好存)


点双联通分量:

void Tarjan(LL x,LL Pre)
{
	Dfn[x] = Low[x] = ++ Cnt;
	S.push( x );
	InStack[x] = 1;
	int l = G[x].size();
	for (Int i = 0; i < l; ++ i)
	{
		LL to = G[x][i];
		if (to == Pre)
			continue;
		if (! Dfn[to])
		{
			Tarjan(to, x);
			Low[x] = Min(Low[x], Low[to]);
		}
		else if ( InStack[to] )
			Low[x] = Min(Low[x], Dfn[to]);
	}
	if (Dfn[x] == Low[x])
	{
		Col ++; LL u;
		do
		{
			Size[Col] ++;
			u = S.top();
			S.pop();
			InStack[u] = 0;
			Bel[u] = Col;
		} while(u != x);
	}
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值