1. 程式人生 > 其它 >割點(模板)

割點(模板)

原題:洛谷 P3388 【模板】割點(割頂)

題目描述

給出一個n個點,m條邊的無向圖,求圖的割點。

輸入格式

第一行輸入兩個正整數 n,m。
下面m行每行輸入兩個正整數x,y表示x到y有一條邊。

輸出格式

第一行輸出割點個數。
第二行按照節點編號從小到大輸出節點,用空格隔開。

題解

板子題,去掉該點後圖不再連通即為割點。
tarjan演算法。

#include<cstdio>
#include<cstring>
#include<map>
#include<queue>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;

const int MAXN=100005,MAXM=200005;

//鄰接表存圖
struct edge{
	int u,v;
}e[MAXM];

int cnt,head[MAXM];

void add(int a,int b){
	e[++cnt].u=b;
	e[cnt].v=head[a];
	head[a]=cnt;
}

//tarjan
int n,m,dfn[MAXN],low[MAXN],id;
bool vis[MAXM];
void tarjan(int x,int fa){
	dfn[x]=low[x]=++id;
	int child=0;//統計子樹
	for(int j=head[x];j!=0;j=e[j].v){
		int nx=e[j].u;//查詢連線的其他點
		if(!dfn[nx]){
			tarjan(nx,fa);
			low[x]=min(low[x],low[nx]);
			if(low[nx]>=dfn[x]&&x!=fa) vis[x]=1;
			//後面的點無法回到x之前的點,說明是割點
			if(x==fa) child++;
		}
		low[x]=min(low[x],dfn[nx]);
	}
	if(child>=2&&x==fa) vis[x]=1;//標記割點
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(NULL);cout.tie(NULL);
	memset(head,0,sizeof(head));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int a,b;
		cin>>a>>b;
		add(a,b);add(b,a);
	}
	int ans=0;
	for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,i);
	//for(int i=1;i<=n;i++) cout<<i<<" "<<dfn[i]<<" "<<low[i]<<endl;
	for(int i=1;i<=n;i++) if(vis[i]==1) ans++;
	cout<<ans<<endl;
	//從小到大輸出節點
	for(int i=1;i<=n;i++) if(vis[i]==1) cout<<i<<" ";
	return 0;
}