(模板)割点
割点(Tarjan)
题意概述
给出一个 个点,
条边的无向图,求图的割点。
思路
:
是第几个被访问到的
:
点通过一条返祖边能回到的$dfn值最小的点是哪一个
- 对于点
, 它的所有儿子中不存在能通过返祖边或者横插边越过它的,那么这个点就是一个割点
- 也就是
,
是父亲,
是儿子,一旦有
,那么
就是一个割点
代码
#include <bits/stdc++.h>
const int maxn = 2e4 + 10, inf = 2e5 + 10;
bool ifcut[maxn] = {0};
int cnt = 0, n = 0, m = 0, u = 0, v = 0, s= 0;
int head[maxn], low[maxn], dfn[maxn];
struct Edge
{
int start;
int terminus;
int next;
}edge[inf];
void AddEdge(int start, int terminus)
{
edge[++s].start = start;
edge[s].terminus = terminus;
edge[s].next = head[start];
head[start] = s;
}
int ans = 0;
int root = 0;
void tarjan(int x)
{
low[x] = dfn[x] = ++cnt;
int flag = 0;
for (int i = head[x]; i != -1; i = edge[i].next)
{
int y = edge[i].terminus;
if (dfn[y] == 0)
{
tarjan(y);
low[x] = std::min(low[x], low[y]);
if (low[y] >= dfn[x])
{
flag++; // x如果是根的话,有一个儿子就会满足一次这个条件,flag就把根的儿子数量记录下来了
if (x != root || flag > 1)
{
ans += (!ifcut[x]);
ifcut[x] = true;
}
}
}
else
{
low[x] = std::min(low[x], dfn[y]);
}
}
}
int main()
{
memset(head, -1, sizeof(head));
memset(ifcut, 0, sizeof(ifcut));
std::cin >> n >> m;
for (int i = 1; i <= m; i++)
{
std::cin >> u >> v;
AddEdge(u, v);
AddEdge(v, u);
}
for (int i = 1; i <= n; i++)
{
if (dfn[i] == 0) root = i, tarjan(i);
}
std::cout << ans << "\n";
for (int i = 1; i <= n; i++)
{
if (ifcut[i])
{
std::cout << i << " ";
}
}
// for (int i = 1; i <= n; i++)
// {
// std::cout << dfn[i] << " ";
// }
return 0;
}