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 - 洛谷

POJ 3177 -- Redundant Paths

// 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);
}

 

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

#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";
  }
}

 

 双连通分量 - OI Wiki

 

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