[模板]割點/割邊

tarjan最重要的之一應該就是dfn和low兩個數組吧,這裡判割邊和割點都是根據的這兩個數組

這裡的思路來自李煜東算法競賽進階指南,好像還有一些其他寫法,但是自己比較菜不會......

注意這裡的寫法cnt(邊的編號)要初始化為1


 

一、割邊

對於割邊的判定,需要:

  搜索樹上存在一個x的子節點y要滿足dfn[x]<low[y]

意思就是如果從x的子樹出發,在不經過邊(x,y)的情況下,不能到達比x更早訪問的節點,就說明邊(x,y)是一個割邊

對於無向圖,因為存的時候按照有向圖存的,每一條無向邊一定是成對存的,所以異或1就能變成另一條邊,

若沿著編號為i的邊遞歸進入了節點x,則忽略從x出發編號為i xor 1的邊,通過其它邊計算low[x](書上抄的)

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100010;
int n,m,num;
int head[maxn],cnt=1;//cnt初始化為1? 
struct node{
    int v,nxt;
}e[maxn*2];
int dfn[maxn],low[maxn];
bool bridge[maxn*2];
void add(int u,int v){
    e[++cnt].v=v;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}
void tarjan(int x,int fa){
    dfn[x]=low[x]=++num;
    for(int i=head[x];i;i=e[i].nxt){
        int y=e[i].v;
        if(!dfn[y]){
            tarjan(y,i);
            low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x])bridge[i]=bridge[i^1]=1;
            //異或是為了處理無向邊,每對無相邊一定是異或的關係 
        }
        else if(i!=(fa^1))
        low[x]=min(low[x],dfn[y]);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,x,y;i<=m;i++){
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i,0);
    for(int i=2;i<cnt;i+=2)
    if(bridge[i])printf("%d %d\n",e[i^1].v,e[i].v);
}

二、割點

對於割點的判定,需要:

   若x不是搜索樹的根節點,則搜索樹上x的一個子節點y要滿足dfn[x]<=low[y]

特別的,如果x是搜索樹的根節點,則x是割點當且僅當搜索樹上存在至少兩個子節點y1,y2滿足上述條件

證明方法遇割邊類似

因為判定條件是小於等於號,所以不必考慮父節點和重邊的問題

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100010;
int n,m,num,root,sum;
int head[maxn],cnt=1;
int dfn[maxn],low[maxn],st[maxn];
struct node{
    int v,nxt;
}e[maxn*2];
void add(int u,int v){
    e[++cnt].v=v;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}
bool cut[maxn];
void tarjan(int x)
{
    dfn[x]=low[x]=++num;
    int fl=0;//兒子個數 
    for(int i=head[x];i;i=e[i].nxt){
        int y=e[i].v;
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if(low[y]>=dfn[x]){//割點判定條件
                fl++;
                if(x!=root || fl>1)cut[x]=1;//根節點必須保證至少子樹有兩個節點 
            }
        }
        else low[x]=min(low[x],dfn[y]);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,x,y;i<=m;i++){
        scanf("%d%d",&x,&y);
        if(x==y)continue;//自環 
        add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++)
    if(!dfn[i])root=i,tarjan(i);
    for(int i=1;i<=n;i++)if(cut[i])sum++;//必須最後再數 
    printf("%d\n",sum);
    for(int i=1;i<=n;i++)
    if(cut[i])printf("%d ",i);
}

 

posted @ 2019-04-12 17:49  羊肉汤泡煎饼  阅读(88)  评论(0编辑  收藏  举报