LGP8435 [LG TPLT] 点双连通分量 学习笔记
LGP8435 [LG TPLT] 点双连通分量 学习笔记
题意简述
给定一个 \(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;
}
浙公网安备 33010602011771号