#include<bits/stdc++.h>
using namespace std;
const int N=2e4+7,M=1e5+7;
int n,m,cnt=0,ind=0;
int dfn[N],low[N],fa[N];
int head[N],to[M*2],nxt[M*2];
bool vis[N];
vector<int> a;
inline void add(int x,int y) {
nxt[++cnt]=head[x];
head[x]=cnt; to[cnt]=y;
}
void tarjan(int u) {
vis[u]=true;
dfn[u]=low[u]=++ind;
int ch=0;
for (int i=head[u];i!=-1;i=nxt[i]) {
int v=to[i];
if (!vis[v]) {
ch++; fa[v]=u;
tarjan(v);
low[u]=min(low[u],low[v]);
if (fa[u]==-1 && ch>=2 || fa[u]!=-1 && low[v]>=dfn[u]) {
a.push_back(u);
}
//if放循环里,只要有一个儿子满足条件即为割点
//对于根节点,计算其子树数量,如果有2棵即以上的子树,就是割点。
//对于非根节点,dfn[]表示访问序数,low[]表示u和u子树中能回溯到的最早的点,如果low[u]>=dfn[u],则u为割点
}
else low[u]=min(low[u],dfn[v]); //访问过的点应比较dfn[]的值
}
}
int main() {
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
for (int i=1;i<=m;i++) {
int x,y;
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
memset(vis,false,sizeof(vis));
for (int i=1;i<=n;i++) {
if (!vis[i]) {
fa[i]=-1; tarjan(i);
}
}
sort(a.begin(),a.end());
a.erase(unique(a.begin(),a.end()),a.end());
printf("%d\n",a.size());
for (int i=0;i<a.size();i++) printf("%d ",a[i]);
return 0;
}
tarjan求割点
猜你喜欢
转载自blog.csdn.net/qq_42149421/article/details/86651852
今日推荐
周排行