割点(模板)

原题:洛谷 P3388 【模板】割点(割顶)

题目描述

给出一个n个点,m条边的无向图,求图的割点。

输入格式

第一行输入两个正整数 n,m。
下面m行每行输入两个正整数x,y表示x到y有一条边。

输出格式

第一行输出割点个数。
第二行按照节点编号从小到大输出节点,用空格隔开。

题解

板子题,去掉该点后图不再连通即为割点。
tarjan算法。

#include<cstdio>
#include<cstring>
#include<map>
#include<queue>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;

const int MAXN=100005,MAXM=200005;

//邻接表存图
struct edge{
	int u,v;
}e[MAXM];

int cnt,head[MAXM];

void add(int a,int b){
	e[++cnt].u=b;
	e[cnt].v=head[a];
	head[a]=cnt;
}

//tarjan
int n,m,dfn[MAXN],low[MAXN],id;
bool vis[MAXM];
void tarjan(int x,int fa){
	dfn[x]=low[x]=++id;
	int child=0;//统计子树
	for(int j=head[x];j!=0;j=e[j].v){
		int nx=e[j].u;//查询连接的其他点
		if(!dfn[nx]){
			tarjan(nx,fa);
			low[x]=min(low[x],low[nx]);
			if(low[nx]>=dfn[x]&&x!=fa) vis[x]=1;
			//后面的点无法回到x之前的点,说明是割点
			if(x==fa) child++;
		}
		low[x]=min(low[x],dfn[nx]);
	}
	if(child>=2&&x==fa) vis[x]=1;//标记割点
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(NULL);cout.tie(NULL);
	memset(head,0,sizeof(head));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int a,b;
		cin>>a>>b;
		add(a,b);add(b,a);
	}
	int ans=0;
	for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,i);
	//for(int i=1;i<=n;i++) cout<<i<<" "<<dfn[i]<<" "<<low[i]<<endl;
	for(int i=1;i<=n;i++) if(vis[i]==1) ans++;
	cout<<ans<<endl;
	//从小到大输出节点
	for(int i=1;i<=n;i++) if(vis[i]==1) cout<<i<<" ";
	return 0;
}

posted @ 2021-10-27 17:14  Chilyyy  阅读(63)  评论(0)    收藏  举报