【模板】割点(割顶)
题目背景
割点
题目描述
给出一个 n n n 个点, m m m 条边的无向图,求图的割点。
输入格式
第一行输入两个正整数 n , m n,m n,m。
下面 m m m 行每行输入两个正整数 x , y x,y x,y 表示 x x x 到 y y y 有一条边。
输出格式
第一行输出割点个数。
第二行按照节点编号从小到大输出节点,用空格隔开。
样例 #1
样例输入 #1
6 7
1 2
1 3
1 4
2 5
3 5
4 5
5 6
样例输出 #1
1
5
提示
对于全部数据, 1 ≤ n ≤ 2 × 1 0 4 1\leq n \le 2\times 10^4 1≤n≤2×104, 1 ≤ m ≤ 1 × 1 0 5 1\leq m \le 1 \times 10^5 1≤m≤1×105。
点的编号均大于 0 0 0 小于等于 n n n。
tarjan图不一定联通。
理论
模板
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N = 1e6+10,M = 2*N;
int e[M],ne[M],w[M],h[N],idx;
bool in_stk[N],is_cut[N];
int stk[N],id[N],tm,top;
int dfn[N],low[N],scc_cnt,scc_size[N];
int n,m;
int root;
vector<int>ans;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void targin(int u){
dfn[u]=low[u]=++tm;
int cct=0;//该点属于几个点联通分量
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(!dfn[j]){
targin(j);
low[u]=min(low[u],low[j]);
if(dfn[u]<=low[j]){
cct++;
if(u!=root||cct>1){
//是割点
is_cut[u]=true;
}
}
}else{
low[u]=min(low[u],dfn[j]);
}
}
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
for(int i=1;i<=n;i++){
if(!dfn[i]){
root=i;
targin(i);
}
}
int res=0;
for(int i=1;i<=n;i++){
if(is_cut[i]){
res++;
}
}
cout<<res<<endl;
for(int i=1;i<=n;i++){
if(is_cut[i]){
cout<<i<<" ";
}
}
return 0;
}