【学习】【割边割点】【Tarjan】

       这是#蒟蒻#对Tarjan求割点和割边的一点小总结,由于水平有限所以欢迎大佬们指出我的错误。

       割点:对于一张无向连通图,删去一个点及与这个点相关的边之后,这个图如果不再连通,那么这个点就是这张图的一个割点。

       割边:对于 一张无向连通图,删去一条边之后,这个图如果不再连通,那么这个点就是这张图的一条割边。

       Tarjan算法求一张无向连通图的所有割点与割边:

       首先,我们用三个数组来记录我们所需要记录的量:Vis[I]记录节点I当前的访问状态:0表示没有访问,1表示在栈中,2表示已经被访问过;Dfn[I]记录的是I节点被访问时的深度,即在该DFS树中的DFS序的编号;Low[I]记录的是I可以到达的访问时间最早的祖先。

       在DFS整个图中,记录每个点的深度。对当前点X,和与X相连的节点Y,存在两种情况:

       (1)Y没有被访问过(Vis[Y]=0),这个时候就递归访问Y点,并用Low[Y]来更新Low[X];

       (2)Y在栈中(Vis[Y]=1),这就说明图中存在一个环,用Dfn[Y]更新Low[X];

       于是,我们根据Dfn,Low的定义可以推出:

       (1)X是割点的条件:①X是根节点,并且儿子节点不只一个②X不是根节点,并且X中的某一个儿子节点Y满足Low[Y]>=Dfn[X];

       (2)X,Y是割边的条件:Low[Y]>Dfn[X]

代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define SG string
#define LL long long
#define Bridge(A,B) Bridge[A][B]
#define Fp(A,B,C,D) for(A=B;A<=C;A+=D)
#define Fm(A,B,C,D) for(A=B;A>=C;A-=D)
#define Clear(A) memset(A,0,sizeof(A))
using namespace std;
const LL Max=1e5+5;
const LL Mod=1e9+7;
const LL Inf=1e18;
LL N,M,PNum,BNum,Low[Max],Dfn[Max],Vis[Max],Point[Max],Bridge[Max][2];
LL Cnt,To[Max<<1],Next[Max<<1],Head[Max];
inline LL Read(){
	LL X=0;char CH=getchar();bool F=0;
	while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
	while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
	return F?-X:X;
}
inline void Write(LL X){
	if(X<0)X=-X,putchar('-');
	if(X>9)Write(X/10);
	putchar(X%10+48);
}
void Insert(LL X,LL Y){
	To[++Cnt]=Y;Next[Cnt]=Head[X];Head[X]=Cnt;
}
void Tarjan(LL X,LL Fa,LL Deep){
	LL I,J,K,Ch=0;
	Vis[X]=1;Dfn[X]=Low[X]=Deep;
	for(I=Head[X];I;I=Next[I]){
		LL Y=To[I];
		if(Y!=Fa&&Vis[Y]==1){
			Low[X]=min(Low[X],Dfn[Y]);
		}
		if(Vis[Y]==0){
			Tarjan(Y,X,Deep+1);++Ch;
			Low[X]=min(Low[X],Low[Y]);
			if((Fa==-1&&Ch>1)||(Fa!=-1&&Low[Y]>=Dfn[X])){
				Point[++PNum]=X;
			}
			if(Low[Y]>Dfn[X]){
				Bridge(++BNum,0)=X;
				Bridge(BNum,1)=Y;
			}
		}
	}Vis[X]=2;
}
int main(){
	LL I,J,K;
	N=Read(),M=Read();
	Fp(I,1,M,1){
		LL X=Read(),Y=Read();
		Insert(X,Y);
		Insert(Y,X);
	}Tarjan(1,-1,0);
	cout<<"割点:"<<endl; 
	Fp(I,1,PNum,1){
		Write(Point[I]);putchar(' ');
	}puts("");
	cout<<"割边:"<<endl; 
	Fp(I,1,BNum,1){
		Write(Bridge(I,0));putchar(' ');Write(Bridge(I,1));puts("");
	}
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值