P3388 割点

【模板】割点(割顶)

题目背景

割点:若删除某点以及其所有连边后,原本其所在图被分为至少两个图,这些图互相不能到达,则该点为割点。

题目描述

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

输入格式

第一行输入两个正整数 \(n,m\)

下面 \(m\) 行每行输入两个正整数 \(x,y\) 表示 \(x\)\(y\) 有一条边。

输出格式

第一行输出割点个数。

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

样例 #1

样例输入 #1

6 7
1 2
1 3
1 4
2 5
3 5
4 5
5 6

样例输出 #1

1 
5

提示

对于全部数据,\(1\leq n \le 2\times 10^4\)\(1\leq m \le 1 \times 10^5\)

点的编号均大于 \(0\) 小于等于 \(n\)

tarjan图不一定联通。

在缩点的tarjan操作基础上我们要统计割点

割点有两类:

1.若点 x为root且子树>=2则为割点

2.x!=root 但 对于 x--->v 有 low[v]>=dfn[x] 则x为割点

注意在统计 root的children时 只有 dfn[v]==0 即未访问过我们才统计 否则scc会重复统计

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
struct Graph{
	int from,nxt,to;
}edge[N<<1];
int head[N],cnt;
inline void add(int u,int v)
{
	cnt++;
	edge[cnt].to=v;
	edge[cnt].from=u;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}
int dfn[N],low[N],cut[N],stack_[N],times,tot,belong[N],pt;
void tarjan(int x,int root)
{
	dfn[x]=low[x]=++times;
	int children=0;
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(!dfn[v])
		{
			tarjan(v,root);
			low[x]=min(low[x],low[v]);
			if(x!=root&&low[v]>=dfn[x])cut[x]=true;
			if(x==root)children++;
		}
		else low[x]=min(low[x],dfn[v]);
	}
	if(x==root&&children>=2)cut[x]=true;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		cin>>x>>y;
		add(x,y);
		add(y,x);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i])tarjan(i,i);
	int ans=0;
	for(int i=1;i<=n;i++)
		if(cut[i]==true)ans++;
	cout<<ans<<"\n";
	for(int i=1;i<=n;i++)
		if(cut[i]==true)
			cout<<i<<" ";
	return 0;
}
posted @ 2023-04-19 21:52  N0zoM1z0  阅读(16)  评论(0编辑  收藏  举报