前言
連通性問題確實時一大比較難啃得蛋糕,每次都要先學習一遍,還不如一次學到通
無向圖的連通性問題
求割點
連通圖:連通圖內的所有點都可以互相到達
割點:將割點刪掉後整張圖不連通
定理1:
一個點s是割點,當且僅當s作為該連通圖的根時,會把連通圖分為不相連的幾部分
定理2:
一個非根節點u是割點,當且僅當u存在一個子節點v不存在一條邊連向u的祖先
一開始我認為只要存在一條邊連向祖先就不算割點,但這裡舉出反例:
實現
考慮dfs序,就是將節點編號為dfs遍歷到的順序
我們設一個dfn表示dfs序,設low表示當前節點能回到的最大祖先的編號,只要存在子節點v的\(low[v]>=dfn[u]\) 就可以了
程式碼
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N=2e5+5;
int n,m,u,v,tot,ans;
int dfn[N],low[N],vis[N],dot[N];
vector<pii>b[N];
void dfs(int x,int nxt){
dfn[x]=low[x]=++tot;
vis[x]=1;
bool cnt=0;
int num=0;
for(auto i:b[x]){
if(nxt==i.second) continue;//這裡大抵是沒有用的
int k=i.first;//要開區域性變數,不要像我一樣手懶開全域性變數寄掉
if(!vis[k]){
num++;
dfs(k,i.second);
low[x]=min(low[x],low[k]);
if(low[k]>=dfn[x]) cnt=1;
}
else{
low[x]=min(low[x],dfn[k]);//無向圖中也可以寫low
}
}
if(!nxt){
if(num>1) dot[++ans]=x;//注意特判根節點的情況
}
else if(cnt){
dot[++ans]=x;
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
b[u].push_back({v,i});
b[v].push_back({u,i});
}
for(int i=1;i<=n;i++){
if(!vis[i]){
dfs(i,0);
}
}
sort(dot+1,dot+ans+1);
printf("%d\n",ans);
for(int i=1;i<=ans;i++){
printf("%d ",dot[i]);
}
}```
###