边双连通分量/割边求法

边双连通分量/割边求法

题目参考 洛谷P1656

算法标签:无向图割边、tarjan强连通分量

如果点u通过一条边m到达v后,v无法在不经过m回到u,那么m这条边称之为割边,u和v分别属于两个不同的双连通分量。易知,两个点是否处于一个双连通分量是等价关系

如何求割边?

记录每个点使用最多一条返祖边能到达的最远祖先,用low[]表示

记录通过dfs访问点的次序序号,用dfn[]表示

  • 如果遍历u出发的边(忽略回去的反向边)到达v时,v已经访问过,说明u可以走这一条返祖边直接回到祖先v,因此更新low[u]=min(low[u],dfn[v]);

    • 否则先dfs(v),尝试通过v回到更远的祖先,即low[u]=min(low[u],low[v])
  • 如果low[v](从v走一条返祖边回到的最远祖先)>dfn[u],那就是说v不能通过其他方式回来u(或者u的祖先)了,也就是u,v是一条割边

如果题目不保证不存在重边,我们是不可以dfs(int u,int fa){```if(v==fa) continue; ```}的因为v可能通过u、v之间的其他重边回到u,

这种情况下,我们在使用链式前向星建图时初始化 边数起点cnt=1,这样每一条边和其反向边的边序号都是^1的关系,2^1=3,3^1=2。dfs里面记录边序号lst,(u->v的一条边e[i]的) i==lst^1时我们就不要访问

#include<iostream>
#include<algorithm>
using namespace std;
#define pii pair<int,int>
#define fi first
#define se second
const int N=310,M=5005;
int cnt0=1;//链式前向星的边数,初始化为1
int head[N];
struct Edge{
    int to,nxt;
}e[M<<1];

void add_edge(int x,int y){
    e[++cnt0].to=y;
    e[cnt0].nxt=head[x];
    head[x]=cnt0;
    e[++cnt0].to=x;
    e[cnt0].nxt=head[y];
    head[y]=cnt0;
}

int n,m;
int st[N],top;//保存强连通分量时的栈
int idx;//强连通分量的编号
int bel[N];//每个点所属哪个双连通分量
int low[N];//最多走一条返祖边回到的最远祖先的dfs序号
int dfn[N],cnt;//dfs序号
int anscnt;
pii ans[M<<1];//存割边

void dfs(int u,int lst){
    low[u]=dfn[u]=++cnt;
    st[++top]=u;
    for(int i=head[u];i;i=e[i].nxt){
        if(i==(lst^1)) continue;//不走反向边
        int v=e[i].to;
        if(dfn[v]==0){
            dfs(v,i);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u]){//v走最多一条其他返祖边回不来u,也即存在割边
                ans[anscnt++]=make_pair(min(u,v),max(u,v));
            }
        }else{
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u]){//相等则说明u是新强连通分量的头(深度最小)
        int v;++idx;
        do{
            v=st[top--];
            bel[v]=idx;
        }while(v!=u);
    }
}

int main(){
    cin>>n>>m;
    for(int i=0,x,y;i<m;i++){
        cin>>x>>y;
        add_edge(x,y);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) dfs(i,-1);
    }
    sort(ans,ans+anscnt);
    for(int i=0;i<anscnt;i++) cout<<ans[i].fi<<' '<<ans[i].se<<endl;
    cout<<endl;
    return 0;
}
posted @ 2026-04-20 21:03  江蝶  阅读(12)  评论(3)    收藏  举报