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 为根节点的子树中,还在栈中的节点就是连通块中的节点

两类缩点归纳:

 

P8436 【模板】边双连通分量 - 洛谷

对于一个 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

 

posted @ 2022-05-28 13:32  董晓  阅读(1752)  评论(0)    收藏  举报