割点(割顶)【tarjan】

>Link

luogu P3388


>Description

求一张无向图的割点集


>解题思路

按dfs序跑一遍tarjan(那边就分成了树边和回边…),分两种情况

  1. 根节点:如果它有两个及以上的子树,显然它就是割点
  2. 非根节点:对于一条边 ( u , v ) (u,v) (u,v),如果 l o w v ≥ d f n u low_v≥dfn_u lowvdfnu,那 u u u 就是割点

>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;

struct edge
{
    
    
	int to, nxt;
} e[N * 2];
int n, m, cnt, h[N], dfn[N], low[N], tot_ans, ans[2 * N], tot;

void add (int u, int v)
{
    
    
	e[++cnt] = (edge){
    
    v, h[u]}; h[u] = cnt;
	e[++cnt] = (edge){
    
    u, h[v]}; h[v] = cnt;
}
void dfs (int now, int fath)
{
    
    
	dfn[now] = low[now] = ++tot;
	int child = 0;
	for (int i = h[now]; i; i = e[i].nxt)
	{
    
    
		int v = e[i].to;
		if (v == fath) continue;
 		if (!dfn[v])
		{
    
    
			dfs (v, now), low[now] = min (low[now], low[v]);
			child++;
			if (low[v] >= dfn[now] && fath)
			  ans[++tot_ans] = now;
		}
		else low[now] = min (low[now], dfn[v]);
	}
	if (!fath && child >= 2) ans[++tot_ans] = now;
}

int main()
{
    
    
	int u, v;
	scanf ("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
    
    
		scanf ("%d%d", &u, &v);
		add (u, v);
	}
	for (int i = 1; i <= n; i++)
	  if (!dfn[i]) dfs (i, 0);
	sort (ans + 1, ans + 1 + tot_ans);
	tot_ans = unique (ans + 1, ans + 1 + tot_ans) - (ans + 1);
	printf ("%d\n", tot_ans);
	for (int i = 1; i <= tot_ans; i++)
	  printf ("%d ", ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43010386/article/details/120899165