D18 eDCC 缩点 Tarjan 算法
D18 eDCC 缩点 Tarjan 算法_哔哩哔哩_bilibili

eDCC:删除割边后的连通块。cnt 条割边,cnt+1 个eDCC
1. 缩点:x → dcc[x],n → cnt
2. 割边即树边:bri[i]=bri[i^1]=1
3. 割边出点的度:du[dcc[to[i]]]++
4. 观察叶节点个数
P2860 [USACO06JAN] Redundant Paths G - 洛谷// Tarjan eDCC缩点 O(n+m) #include<bits/stdc++.h> using namespace std; const int N=5010,M=20010; int n,m,a,b; int h[N],to[M],ne[M],idx=1; void add(int a,int b){ to[++idx]=b,ne[idx]=h[a],h[a]=idx; } int dfn[N],low[N],tim,stk[N],top,dcc[N],cnt; int bri[M],du[N]; void tarjan(int x,int ine){ dfn[x]=low[x]=++tim; stk[++top]=x; for(int i=h[x];i;i=ne[i]){ int y=to[i]; if(!dfn[y]){ //若y未访问 tarjan(y,i); low[x]=min(low[x],low[y]); if(dfn[x]<low[y]) bri[i]=bri[i^1]=1; } else if(i!=(ine^1)) //若y已访问且不是反边 low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ //若x是dcc的根 ++cnt; while(1){ int y=stk[top--]; dcc[y]=cnt; if(y==x)break; } } } int main(){ cin>>n>>m; while(m--){ cin>>a>>b,add(a,b),add(b,a); } tarjan(1,0); for(int i=2;i<=idx;i++) //枚举边,统计出点度数 if(bri[i]) du[dcc[to[i]]]++; int sum=0; for(int i=1;i<=cnt;i++) //枚举dcc,统计叶节点数 if(du[i]==1) sum++; cout<<(sum+1>>1); }
#include<bits/stdc++.h> using namespace std; const int N=500010,M=4000010; int n,m,a,b; int h[N],to[M],ne[M],idx=1; void add(int a,int b){ to[++idx]=b,ne[idx]=h[a],h[a]=idx; } int dfn[N],low[N],tim,stk[N],top,dcc[N],siz[N],cnt,bri[M]; vector<int> d[N]; void tarjan(int x,int ine){ dfn[x]=low[x]=++tim;stk[++top]=x; for(int i=h[x];i;i=ne[i]){ int y=to[i]; if(!dfn[y]){ tarjan(y,i); low[x]=min(low[x],low[y]); if(low[y]<dfn[x]) bri[i]=bri[i^1]=1; } else if(i!=(ine^1)) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ ++cnt; while(1){ int y=stk[top--]; // dcc[y]=cnt; siz[cnt]++; d[cnt].push_back(y); if(x==y) break; } } } int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n>>m; while(m--){ cin>>a>>b;add(a,b);add(b,a); } for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i,0); cout<<cnt<<"\n"; for(int i=1;i<=cnt;i++){ cout<<siz[i]<<" "; for(int j:d[i]) cout<<j<<" "; cout<<"\n"; } }
浙公网安备 33010602011771号