Tarjan算法与无向图连通性

tarjan算法在无向图中概念较多,包括割点、桥、点双连通分量、边双连通分量等。

割点

割点判定法则:对于一个节点u,当且仅当u存在一个子节点j,使得dfn[u] ≤ \leq low[j]。特别地,如果u是搜索树中的根节点,那么当且仅当u存在两个子节点j1,j2,使得dfn[u] ≤ \leq min(low[j1],low[j2])。
求割点的模板

#include<iostream>
using namespace std;
const int N=2e4+5,M=2e5+5;
int head[N],to[M],ne[M],idx;
void add(int x,int y){
	ne[++idx]=head[x];
	to[idx]=y;
	head[x]=idx;
}
int dfn[N],low[N],tmp;
int root,cut[N];
void tarjan(int u){
	dfn[u]=low[u]=++tmp;
	int flag=0;
	for(int i=head[u];i;i=ne[i]){
		int j=to[i];
		if(!dfn[j]){
			tarjan(j);
			low[u]=min(low[u],low[j]);
			if(dfn[u]<=low[j]){
				flag++;
				if(u!=root || flag>1) cut[u]=true;
			}
		}
		else low[u]=min(low[u],dfn[j]);
		//因为判定法则可以取等号,所以不用考虑父节点以及重边的情况
	}
}
int main(){
	int n,m;
	cin>>n>>m;
	while(m--){
		int x,y;
		cin>>x>>y;
		add(x,y);add(y,x);
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			root=i;
			tarjan(i);
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		if(cut[i]) ans++;
	}
	cout<<ans<<endl;
	for(int i=1;i<=n;i++){
		if(cut[i]) cout<<i<<' ';
	}
	return 0;
}

例题1:AcWing 363.B城
本题需要考虑删去点i对于图的连通性的影响:若点i不是割点,那么答案是2(n-1);若点i是割点,那么图将被划分为若干个连通块。由割点的性质容易知道:连通块为点i、点i的子树、点i的上层节点。那么每个连通块中的任意一个点,都会和连通块外的任意一点构成一对不连通点对。所以只需要在计算割点的同时统计答案即可,代码如下:

#include<iostream>
using namespace std;
const int N=1e5+5,M=1e6+5;
typedef long long ll;
int n,m;
int head[N],to[M],ne[M],idx;
void add(int x,int y){
    ne[++idx]=head[x];
    to[idx]=y;
    head[x]=idx;
}
bool cut[N];
int dfn[N],low[N],tmp;
ll siz[N],ans[N];
void tarjan(int u){
    low[u]=dfn[u]=++tmp;siz[u]=1ll;
    ll sum=0ll;
    for(int i=head[u];i;i=ne[i]){
        int j=to[i];
        if(!dfn[j]){
            tarjan(j);
            siz[u]+=siz[j];
            low[u]=min(low[u],low[j]);
            if(low[j]>=dfn[u]){//割点判定法则
                cut[u]=true;
                ans[u]+=(n-siz[j])*siz[j];
                sum+=siz[j];
            }
        }
        else low[u]=min(low[u],dfn[j]);
    }
    if(cut[u]){
        ans[u]+=(n-1-sum)*(sum+1)+n-1;
    }
    else ans[u]=2*(n-1);
}
int main(){
    cin>>n>>m;
    while(m--){
        int x,y;
        cin>>x>>y;
        add(x,y);add(y,x);
    }
    tarjan(1);
    for(int i=1;i<=n;i++) cout<<ans[i]<<endl;
    return 0;
}

桥、边双连通分量

桥(割边)判定法则:dfn[u]<low[j]
桥的判定法则较为麻烦,需要考虑父节点的情况,而且若出现重边,那么该两点间一定不存在桥。
桥的性质:一定是搜索树上的一条边
代码如下

在这里插入代码片
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值