1. 程式人生 > 實用技巧 >邊雙連通

邊雙連通


/*
 * 求邊雙連通分量
 * 整體思路就是在圖上dfs 並記錄時間戳
 * 相較於有向圖 只需要注意一條邊的兩個節點即可
 */






#include <bits/stdc++.h>
#define N 1000010
#define p(a) putchar(a)
using namespace std;
int n,m;

struct edge{
    int to,from,next;
}e[N<<1];int head[N],cntedge=1;//前向星存邊,注意邊從二開始
void addedge(int from,int to){
    e[++cntedge]={to,from,head[from]};
    head[from]=cntedge;
    swap(from,to);
    e[++cntedge]={to,from,head[from]};
    head[from]=cntedge;
};

int f[N];//記錄點是由哪條邊訪問
int dfn[N],low[N],num,cnt,from[N],cut[N];//dfn:tarjan時間戳   low:tarjan的low num:時間戳 計數器, from[i]:i號節點的連通分量 cnt:連通分量的個數 cut[i]:i號邊是否為橋邊
int tarjan(int u,int fa){
    dfn[u]=low[u]=++num;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(!dfn[v]){
            f[v]=i>>1,tarjan(v,u);
            low[u]=min(low[v],low[u]);
        }else if(v!=fa){//
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(f[u]&&low[u]==dfn[u]){
        cut[f[u]]=1;
    }
}
void dfs(int x,int y){
    from[x]=y;
    for(int i=head[x];i;i=e[i].next){
        int v=e[i].to;
        if(!from[v]&&!cut[i>>1]){
            dfs(v,y);
        }
    }
}
signed main(){
    in(n);in(m);
    for(int x,y,i=1;i<=m;i++){
        in(x);in(y);
        addedge(x,y);
    }
    tarjan(1,0);
    for(int i=1;i<=n;i++){
        if(!from[i])dfs(i,++cnt);
    }
    return 0;
}