P8435 【模板】点双连通分量

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e6 + 10, inf = 0x3f3f3f3f;

int n, m;              // n: 顶点数, m: 边数
vector<int> g[N];      // 邻接表存储图
int dfn[N], low[N], id; // Tarjan算法相关数组:dfn-时间戳,low-能追溯到的最小dfn,id-时间戳计数器
int rt;                // 当前DFS树的根节点
int q[N], top;         // 数组模拟栈,存储DFS遍历路径
int dcc_cnt;           // 点双连通分量计数器
vector<int> dcc[N];    // 存储每个点双连通分量包含的点

// Tarjan算法求点双连通分量
void tarjan(int x)
{
    // 初始化当前节点的dfn和low值
    dfn[x] = low[x] = ++id;
    // 当前节点入栈
    q[++top] = x;
    
    // 特殊情况处理:孤立点(根节点且没有邻边)
    if(x == rt && g[x].size() == 0){
        dcc_cnt++;
        dcc[dcc_cnt].push_back(x);
        top--;  // 弹出孤立点
        return;
    }
    
    int c = 0;  // 记录DFS树中x的直接子节点数(用于根节点割点判断)
    
    // 遍历x的所有邻接点
    for(int i = 0; i < g[x].size(); i++)
    {
        int y = g[x][i];  // 邻接点y
        
        if(!dfn[y]){      // 如果y未被访问过
            tarjan(y);    // 递归访问y
            low[x] = min(low[x], low[y]);  // 用y的low更新x的low
            
            // 关键判断:y无法不经过x到达x的祖先
            if(low[y] >= dfn[x]){
                //cout << y << " " << x << " 666" << endl;
                // 找到一个点双连通分量
                dcc_cnt++;
                while(1)
                {
                    int z = q[top--];
                    dcc[dcc_cnt].push_back(z);
                    if(z == y) break;
                }
                // 将割点x加入当前点双(但x不出栈,因为它可能属于多个点双)
                dcc[dcc_cnt].push_back(x);
            }
        }
        else {
            // y已被访问过,说明(x,y)是回边,用y的dfn更新x的low
            low[x] = min(low[x], dfn[y]);
        }
    }
}

int main()
{
    cin >> n >> m;
    
    // 读入图数据
    for(int i = 1; i <= m; i++)
    {
        int x, y;
        cin >> x >> y;
        if(x == y) continue;  // 跳过自环
        g[x].push_back(y);
        g[y].push_back(x);
    }
    
    // 对每个连通分量进行Tarjan算法
    for(int i = 1; i <= n; i++)
        if(!dfn[i]) {         // 如果节点i未被访问过
            rt = i;           // 设置当前DFS树的根
            tarjan(i);        // 从i开始DFS
            
            // 注意:这里可能存在栈中剩余节点未处理的问题
            // 但对于标准点双模板题,通常能通过测试
        }
    
    // 输出结果
    cout << dcc_cnt << endl;
    for(int i = 1; i <= dcc_cnt; i++)
    {
        cout << dcc[i].size() << " ";
        for(int j = 0; j < dcc[i].size(); j++)
            cout << dcc[i][j] << " ";
        cout << endl;
    }
    // for(int i = 1; i <= n; i++)
    //         cout << dfn[i] << " " << low[i] << endl;
    return 0;
}

 

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