题目描述
给出一个n个点,m条边的无向图,求图的割点。
【模板】传送门
输入格式
第一行输入两个正整数 n,m.
下面 mm 行每行输入两个正整数x,y表示x到y有一条边。
输出格式
第一行输出割点个数。
第二行按照节点编号从小到大输出节点,用空格隔开。
输入输出样例
输入 #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
≤
m
≤
1
0
5
{1\leq n \le 2\times 10^4 ,1\leq m \le 10^5}
1≤n≤2×104,1≤m≤105。
点的编号均大于0小于等于n。
t
a
r
j
a
n
{tarjan}
tarjan图不一定联通。
说明
tarjan算法模板题,复杂度 O ( m + n ) {O(m+n)} O(m+n).
代码
#include<bits/stdc++.h>
#define N 400005//开小了会WA和RE!
#define in read()
using namespace std;
int n,m,tot,root,ans;
int fi[N],nxt[N],to[N];
int dfn[N],low[N],flag[N],inde=0;//flag标记节点i是否为割点.
//dfn[i]代表节点i被遍历到的次序,时间戳不可改变.
//low[u]表示该子树中,且仍在栈中的最小时间戳.
inline int in{
int i=0;char ch;
while(!isdigit(ch)){ch=getchar();}
while(isdigit(ch)){i=(i<<3)+(i<<1)+(ch-'0');ch=getchar();}
return i;
}
inline int lian(int u,int v)
{
nxt[tot]=fi[u];
fi[u]=tot;
to[++tot]=v;
}
inline void Dfs(int u,int fa)//tarjan割点
{
int son=0;
inde++;
dfn[u]=inde;
low[u]=inde;
for(int i=fi[u];i;i=nxt[i])
{
int v=to[i];
if(dfn[v]==0)
{
Dfs(v,fa);
low[u]=min(low[u],low[v]);
if(u!=fa&&low[v]>=dfn[u])flag[u]=1;
if(u==fa)son++;
}
low[u]=min(low[u],dfn[v]);
}
if(son>=2&&u==fa)flag[u]=1;
return;
}
int main()
{
int x,y;
n=in,m=in;
tot=0;
for(int i=1;i<=m;i++)
{
x=in,y=in;
lian(x,y);
lian(y,x);
}
root=-1;
for(int i=1;i<=n;i++)
if(dfn[i]==0)Dfs(i,i);
for(int i=1;i<=n;i++)
if(flag[i])ans++;
printf("%d\n",ans);
for(int i=1;i<=n;i++)
if(flag[i]) printf("%d ",i);
cout<<endl;
return 0;
}
(若不太明白tarjan算法的大佬萌新们可以点击缩点模板)
另外推荐一道很好的割点题[HNOI2012]矿场搭建!qwq(有点难哦)