LGP8435 [LG TPLT] 点双连通分量 学习笔记

LGP8435 [LG TPLT] 点双连通分量 学习笔记

Luogu Link

题意简述

给定一个 \(n\)\(m\) 边的无向图。求其所有点双连通分量。

做法解析

我们用 \(\texttt{tarjan}\) 算法求解。对于点双,一言以蔽之,如果你发现在 \(\texttt{dfs}\) 搜索树上,点 \(u\) 子树里的若干点的 \(\texttt{low}\) 值都越不过 \(u\),就说明可以收网一个点双了(因为这相当于把 \(u\) 砍了之后这些点就与 \(u\) 往外的点失联了)。

注意你在收网一个点双的时候,它的根部是可能属于其它点双的,所以不要把它立即出栈。

注意因为你是在遍历 \(v\) 的时候收网点双的,所以对于原图上的孤立点我们用一行特判。

代码实现

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=5e5+5;
int N,M,X,Y;
vector<int> Gr[MaxN],bcc[MaxN];
void addudge(int u,int v){
    Gr[u].push_back(v);
    Gr[v].push_back(u);
}
int tot,dfn[MaxN],low[MaxN],stk[MaxN],ktp,bcnt;
void tarjan(int u,int f){
    dfn[u]=low[u]=++tot;
    stk[++ktp]=u;int vc=0;
    for(int v : Gr[u]){
        if(!dfn[v]){
            vc++,tarjan(v,u);
            minner(low[u],low[v]);
            if(low[v]>=dfn[u]){
                bcnt++;
                while(stk[ktp+1]!=v)bcc[bcnt].push_back(stk[ktp--]);
                bcc[bcnt].push_back(u);
            }
        }
        else if(v!=f)minner(low[u],dfn[v]);
    }
    if(!f&&!vc)bcc[++bcnt].push_back(u);
}
int main(){
    readis(N,M);
    for(int i=1;i<=M;i++)readis(X,Y),addudge(X,Y);
    for(int i=1;i<=N;i++)if(!dfn[i])ktp=0,tarjan(i,0);
    writil(bcnt);
    for(int i=1;i<=bcnt;i++){
        writip(bcc[i].size());
        for(int j : bcc[i])writip(j);
        puts("");
    }
    return 0;
}
posted @ 2025-08-01 10:58  矞龙OrinLoong  阅读(5)  评论(0)    收藏  举报