题目背景
割点
题目描述
给出一个 nn 个点,mm 条边的无向图,求图的割点。
输入格式
第一行输入两个正整数 n,mn,m。
下面 mm 行每行输入两个正整数 x,yx,y 表示 xx 到 yy 有一条边。
输出格式
第一行输出割点个数。
第二行按照节点编号从小到大输出节点,用空格隔开。
输入输出样例
输入 #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^41≤n≤2×10
4
,1\leq m \le 1 \times 10^51≤m≤1×10
5
。
点的编号均大于 00 小于等于 nn。
tarjan图不一定联通。
分析:
可以用 tarjan 算法求割点
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e4+10,M = 2e5;
int n,m;
int h[N],ne[M],e[M],idx;
int dfn[N],low[N],num,ans[N],pre[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u)
{
dfn[u]=low[u]=++num;
int son=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int v=e[i];
if(!dfn[v])
{
pre[v]=u;
tarjan(v);
low[u]=min(low[u],low[v]);
son++;
if(pre[u]!=0&&low[v]>=dfn[u]) ans[u]=1;
}
else if(pre[u]!=v)
low[u]=min(low[u],dfn[v]);
}
if(pre[u]==0&&son>1) ans[u]=1;
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof(h));
int a,b;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i]) tarjan(i);
}
int k=0;
for(int i=1;i<=n;i++)
if(ans[i]) k++;
printf("%d\n",k);
for(int i=1;i<=n;i++)
{
if(ans[i])
{
printf("%d ",i);
}
}
return 0;
}