D18【模板】eDCC 缩点 Tarjan 算法
D18【模板】eDCC 缩点 Tarjan 算法_哔哩哔哩_bilibili
参考:D17【模板】割边 Tarjan 算法 - 董晓 - 博客园
对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为割边(桥)
在一张连通的无向图中,对于两个点 x 和 y,如果无论删去哪一条边,都不能使它们不连通,就说 x 和 y 边双连通
对于一个无向图中的 极大 边双连通的子图,称这个子图为一个 边双连通分量(eDCC)

图中的 eDCC:{1,2,3}, {4}, {5,6}, {7}, {8},缩点后为 5 个点
1. 将所有割边打标记
2. 用一个栈存点,如果遍历完 x 发现 low[x]==dfn[x],说明以 x 为根节点的子树中,还在栈中的节点就是连通块中的节点
两类缩点归纳:

对于一个 n 个节点 m 条无向边的图,请输出其边双连通分量的个数,并且输出每个边双连通分量
// eDCC缩点 Tarjan算法 O(n+m) #include<bits/stdc++.h> using namespace std; const int N=500010,M=4000010; int to[M],ne[M],h[N],idx=1; //2,3异或配对 void add(int a,int b){ to[++idx]=b,ne[idx]=h[a],h[a]=idx; } int n,m; int dfn[N],low[N],stk[N],top,cnt; // bool bri[M]; //标记桥 vector<int> dcc[N]; //记录eDCC内的点 void tarjan(int x,int e){ dfn[x]=low[x]=++dfn[0]; 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(low[y]>dfn[x]) bri[i]=bri[i^1]=1; } else if(i!=(e^1)) //若y已访问且不是反边 low[x]=min(low[x],dfn[y]); } if(low[x]==dfn[x]){ //若x是edcc的根 ++cnt; while(stk[top+1]!=x) dcc[cnt].push_back(stk[top--]); } } int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n>>m; for(int a,b;m--;)cin>>a>>b,add(a,b),add(b,a); for(int i=1;i<=n;i++)if(!dfn[i]) tarjan(i,0); //eDCC缩点 cout<<cnt<<"\n"; for(int i=1;i<=cnt;i++){ //枚举缩点 cout<<dcc[i].size()<<" "; for(int j:dcc[i]) cout<<j<<" "; cout<<"\n"; } }
例题:POJ 3177 -- Redundant Paths
浙公网安备 33010602011771号