D17【模板】割边 Tarjan 算法

D17【模板】割边 Tarjan 算法_哔哩哔哩_bilibili

 

参考:D16【模板】割点 Tarjan 算法 - 董晓 - 博客园

对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为割边(桥)

割边判定法则:当搜索树上存在 x 的一个子节点 y,满足 low[y]>dfn[x],则 (x,y)这条边就是割边

low[y]>dfn[x],说明从 y 出发,在不经过 (x,y) 这条边的前提下,不管走哪条边,都无法到达 x 或 更早访问的节点。故删除 (x,y) 这条边,以 y 为根的子树 subtree(y) 也就断开了。即环外的边割得断

反之,若low[y]<=dfn[x],则说明 y 能绕行其他边到达 x 或更早访问的节点,(x,y) 就不是割边了。即环内的边割不断

image

 

T103481 【模板】割边 - 洛谷

#include<bits/stdc++.h>
using namespace std;

const int N=50005,M=600005;
int n,m;
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 dfn[N],low[N],cnt;
struct Bri{int x,y;}bri[M]; //割边

void tarjan(int x,int e){
  dfn[x]=low[x]=++dfn[0];
  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]) //若low[y]≤dfn[x],说明y可以绕回x或祖先 bri[cnt++]={x,y}; //记录桥端点 } else if(i!=(e^1)) //若y已访问且不是反边 low[x]=min(low[x],dfn[y]); } } int main(){ 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); cout<<cnt; }

 

P1656 炸铁路 - 洛谷

#include<bits/stdc++.h>
using namespace std;

const int N=210,M=10010;
int n,m;
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 dfn[N],low[N],cnt;
struct Bri{int x,y;}bri[M]; //割边

void tarjan(int x,int e){
  dfn[x]=low[x]=++dfn[0];
  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[cnt++]={x,y}; //记录桥端点
    }
    else if(i!=(e^1)) //若y已访问且不是反边
      low[x]=min(low[x],dfn[y]);
  }
}
int main(){
  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);
  
  sort(bri,bri+cnt,[&](Bri a,Bri b){
    if(a.x==b.x) return a.y<b.y;
    return a.x<b.x;});
    
  for(int i=0;i<cnt;i++)cout<<bri[i].x<<" "<<bri[i].y<<"\n";
}

 

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