~~~~~ P8435 【模板】点双连通分量 ~~~~~ 总题单链接
~~~~~ 建议看完割点后食用。
思路
~~~~~ 什么是点双连通分量?就是不包含割点的极大连通块。
~~~~~ 我们对割点要裂点,有多少个点双连通分量包含了它,它就要裂成几个点。
~~~~~ 怎么找点双连通分量?就是割点的时候将遍历到的点放到一个桶里,在遍历 u u u 的邻点 v v v 时,若 v v v 的时间戳 ≥ \geq ≥ u u u 的时间戳,那当前桶内的点和 u u u 就是一个点双连通分量。
模版题代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,tot,cnt,stk[500005];
ll dfn[500005],low[500005],top;
vector<ll>eg[500005],scc[500005];
void Tarjan(ll p){
if(eg[p].empty()){
scc[++cnt].push_back(p);
return;
}
ll cld=0;
stk[++top]=p;
dfn[p]=low[p]=++tot;
for(ll v:eg[p]){
if(!dfn[v]){
Tarjan(v);
low[p]=min(low[p],low[v]);
if(low[v]>=dfn[p]){
cnt++;
while(1){
ll z=stk[top--];
scc[cnt].push_back(z);
if(z==v)break;
}
scc[cnt].push_back(p);
}
}
else low[p]=min(low[p],dfn[v]);
}
}
signed main(){
ios::sync_with_stdio(false);
cin>>n>>m;
while(m--){
ll x,y;cin>>x>>y;
if(x==y)continue;
eg[x].push_back(y);
eg[y].push_back(x);
}
for(ll i=1;i<=n;i++)
if(!dfn[i])Tarjan(i);
cout<<cnt<<endl;
for(ll i=1;i<=cnt;i++){
cout<<scc[i].size()<<" ";
for(ll it:scc[i])cout<<it<<" ";
cout<<endl;
}
return 0;
}