(模板)割点

割点(Tarjan)

题意概述

给出一个 个点, 条边的无向图,求图的割点。

思路

  1. 是第几个被访问到的
  2. : 点通过一条返祖边能回到的$dfn值最小的点是哪一个
  3. 对于点, 它的所有儿子中不存在能通过返祖边或者横插边越过它的,那么这个点就是一个割点
  4. 也就是, 是父亲,是儿子,一旦有,那么就是一个割点

代码

#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;
}
全部评论

相关推荐

用微笑面对困难:这里面最强的是驾驶证了,可以入职美团大厂,然后直接开启黄马褂人生
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务