对于某个无向图
割点又称割顶,如果删掉这个点,图不连通,那么这个点就是一个割点
桥、如果删掉这个边,图不连通,那么这个边就是一个桥
1、对于根节点,如果有一个以上子树,那么就是割点
2、对于非根节点,dfn[u]表示时间戳,即dfs到第几次访问到u点,
low[u]表示顶点u及其子树中的点,通过非父子边(回边,即dfs树多出来的边),能够回溯到的最早的点(dfn最小)的dfn值
(但不能通过连接u与其父节点的边)。对于边(u, v),如果low[v]>=dfn[u],此时u就是割点。
桥:low[v]>dfn[u]则 u到v(v到u) 是 桥
模板、割点、洛谷p3388
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+7;
struct node{int val,next;}E[(int)1e6+7];
int n,m,x,y,tot,cnt = 1,ans,head[maxn],low[maxn],dfn[maxn];
bool vis[maxn];
void add(int u,int v){E[cnt] = {v,head[u]},head[u] = cnt++;}
void tarjan(int u,int Fa,int cnt = 0){
low[u] = dfn[u] = ++tot;
for(int i=head[u];i;i = E[i].next){
int v = E[i].val;
if(!dfn[v]){
tarjan(v,u),low[u] = min(low[u],low[v]);
if(u == Fa)cnt++;
if(low[v]>=dfn[u] && u != Fa)vis[u] = true;//割点
//if(low[v]>dfn[u]) 桥 u->v || v->u
}else if(dfn[v]<dfn[u] && v != Fa)//单纯的求割点的话这个elseif可以不写
low[u] = min(low[u],dfn[v]);
}
if(cnt>=2 && u==Fa)vis[u] = true;
}
signed main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i,i);
for(int i=1;i<=n;i++)if(vis[i])ans++;
printf("%d\n",ans);
for(int i=1;i<=n;i++)if(vis[i])printf("%d ",i);
return 0;
}
红书代码、