P3388 【模板】割点(割顶)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 10,inf = 0x3f3f3f3f;
vector<int> g[N];  // 邻接表存储图
int dfn[N],low[N],id;  // dfn:时间戳,low:能回溯到的最小dfn,f:父节点,id:时间戳计数器
int vis[N];  // 标记节点是否为割点
int ans;     // 割点数量
int n,m;     // 顶点数,边数

void tarjan(int x,int fa)  // Tarjan算法,x:当前节点,fa:父节点
{
    dfn[x] = low[x] = ++id;  // 初始化当前节点的dfn和low值
    int cnt = 0;             // 记录当前节点的子节点数量(用于判断根节点是否为割点)
    
    for(int i = 0; i < g[x].size(); i++)
    {
        int y = g[x][i];  // 邻接节点y
        if(dfn[y] == 0){  // 如果y未被访问过(树边)
            cnt++;        // 子节点计数+1
            tarjan(y,x);  // 递归遍历子节点    
            low[x] = min(low[x],low[y]);  // 更新当前节点的low值
            // 判断割点条件:
            if(fa == 0){  // 如果x是根节点
                if(cnt >= 2) vis[x] = 1;  // 根节点有>=2个子树则是割点
            }
            else if(low[y] >= dfn[x]) vis[x] = 1;  // 非根节点:存在子节点y满足low[y]>=dfn[x]
        }
        else low[x] = min(low[x],dfn[y]);  // 回边,更新low值
    }
}

int main()
{
    cin >> n >> m;
    // 读入图数据,构建无向图
    for(int i = 1; i <= m; i++)
    {
        int x,y; cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    
    // 遍历所有连通分量(因为图可能不连通)
    for(int i = 1; i <= n; i++)
    {
        if(dfn[i] == 0)  // 如果节点i未被访问过
            tarjan(i,0); // 从i开始Tarjan算法,父节点设为0(表示根节点)
    }
    
    // 统计割点数量
    for(int i = 1; i <= n; i++)
        if(vis[i]) ans++;
    
    // 输出结果
    cout << ans << endl;
    for(int i = 1; i <= n; i++)
        if(vis[i])
            cout << i << " ";
    return 0;
}

 

posted @ 2026-03-28 20:29  CRt0729  阅读(20)  评论(0)    收藏  举报