题解:[模板] 割点

本蒟蒻一下tarjan算法,然后就不知死活的觉得自己应该都会了,然后就在割点上卡了几天捂脸,来发一篇题解

其实,Tarjan算法的精髓是dfn和low

dfn[i]就是时间戳,即在什么时刻搜索到了点i

low[i]则是i点能回溯到的dfn最小的祖先

low[a]=min(low[a],dfn[p]);

ps:

给的图不一定是连通图(卡的我啊啊啊)

链式前向星开边要2倍(这个应该都知道吧)
#include <algorithm>
#include <iostream>
#include <cstdio>
#define FOR(i,n,m) for(int i=n;i<=m;++i)
#define min(a,b) (a<b?a:b)
#define re register
#define gc getchar()
using namespace std;
const int N=100010;
inline int read() {
	re int x(0),f(1);
	re char ch=gc;
	while(ch<'0'||ch>'9') {
		if(ch=='-') f=-1;
		ch=gc;
	}
	while(ch>='0'&&ch<='9') {
		x=(x<<1)+(x<<3)+(ch^48);
		ch=gc;
	}
	return x*f;
}

//前向星
struct node {
	int next,to;
} e[N<<1];
int h[N<<1],n,m,cnt;
#define add(u,v) e[++cnt].next=h[u],e[cnt].to=v,h[u]=cnt;
#define QXX(u) for(int i=h[u],v;v=e[i].to,i;i=e[i].next)
//上面都没什么用


//tarjan
int dfn[N],low[N],vis[N],s[N],tot,top;
/*
dfn	:时间戳
low	:i及i的子树中所有结点能到达的结点中dfn最小的结点的时间戳
*/ 
void tarjan(int u,int fa) {
//	cout<<u<<" "; 
    int son=0;//以x为根的子树0的个数 
	dfn[u]=low[u]=++tot;
	QXX(u) {
//		cout<<v<<" ";
		if(!dfn[v]) {
			tarjin(v,u);
			low[u]=min(low[u],low[v]);
			//更新当前节点的low值
			if(u==fa) ++son;
			if(u!=fa&&low[v]>=dfn[u]) vis[u]=1;
			//如果一个点不是根,并且它的儿子在不经过它的情况下无法回到它的父亲,那么它也是割点 
		}
		else if(v!=fa) low[u]=min(low[u],dfn[v]);
		//如果一个点被搜索过了并且是x的儿子或者是它的父亲,就可以直接回溯 
	}
	if(u==fa&&son>=2) vis[u]=1;
	//如果一个点是根并且有多于两个子树,就是割点
}


void work() {
	scanf("%d%d",&n,&m);
	FOR(i,1,m) {
		int x,y;
		x=read(),y=read();
		add(x,y) add(y,x);
	}
	FOR(i,1,n) if(!dfn[i]) tarjin(i,i);
	int Tot=0,ans[N]; 
	FOR(i,1,n) if(vis[i]) ans[++Tot]=i;
	cout<<Tot<<endl;
	FOR(i,1,Tot) cout<<ans[i]<<" "; 
}

int main() {

	work();

	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43464026/article/details/88127858