連通性問題大雜燴

daydreamer_zcxnb發表於2024-10-12

前言

連通性問題確實時一大比較難啃得蛋糕,每次都要先學習一遍,還不如一次學到通

無向圖的連通性問題

求割點

連通圖:連通圖內的所有點都可以互相到達
割點:將割點刪掉後整張圖不連通

定理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]);
	}
}```
###

相關文章