tarjan进阶

模板向。

以前学的scc已经差不多可以倒着打了。求scc和缩点还是比较好搞的(当然,必经点和边确实没学)。

现在学一学求eDcc和vDcc的tarjan。

求eDcc,只要学会求桥,然后dfs染色即可,至于缩点就是枚举每条边然后新建图。

桥的判定要low[y]>dfn[x],因为只有这样才不能回到dfs树在x以上的位置。而且由于一些重边问题,tarjan开第二参量,确定low值不被自己更新,需要用到邻接表成对变换。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
int read(){
    int sum=0,f=1;char x=getchar();
    while(x<'0'||x>'9'){
        if(x=='-') f=-1;
        x=getchar();
    }while(x>='0'&&x<='9'){
        sum=sum*10+x-'0';
        x=getchar();
    }return sum*f;
}
struct EDGE{
    int ed,nex;
}edge[100],edgec[100];
int first[50],firstc[50],num=1,numc;
int n,m;
int dfn[50],low[50],bl[50];
int ord,ecc;
bool bridge[100];
void add(int st,int ed){
    edge[++num].ed=ed;
    edge[num].nex=first[st];
    first[st]=first[st];
}
void addc(int st,int ed){
    edgec[++numc].ed=ed;
    edgec[numc].nex=firstc[st];
    firstc[st]=numc;
}
void tarjan(int x,int in_edge){
    dfn[x]=low[x]=++ord;
    for(int i=first[x];i;i=edge[i].nex){
        int y=edge[i].ed;
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x])
                bridge[i]=bridge[i^1]=1;
        }else if(i!=(in_edge^1))
            low[x]=min(low[x],dfn[y]);
    }
}
void dfs(int x){
    bl[x]=ecc;
    for(int i=first[x];i;i=edge[i].nex){
        int y=edge[i].ed;
        if(bl[y]||bridge[i]) continue;
        dfs(y);
    }
}
int main(){
    n=read();m=read();
    for(int i=1,x,y;i<=m;i++){
        x=read();y=read();
        add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i]) tarjan(i,0);//求桥
    for(int i=1;i<=n;i++)
        if(!bl[i]){
            ecc++;
            dfs(i);
        }
    for(int i=2;i<=num;i++){
        int x=edge[i].ed,y=edge[i^1].ed;
        if(bl[x]==bl[y]) continue;
        addc(bl[x],bl[y]);
    }
}
View Code

求vDcc,要学会求割点,在求割点的程序里套上一个用vector存vDcc的子块即可。

割点的判定要low[y]>=dfn[x],而且因为搜索树的性质,需要单独判断根的情况。

缩点的时候因为一个割点可以在两个vDcc里,所以先将割点单独编号,然后建新边。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
struct EDGE{
    int ed,nex;
}edge[200],edgec[200];
int first[100],firstc[100];
int n,m,ord,top,root,vccnum,nvcn;
int dfn[100],low[100],sta[101000],new_id[100],bl[100];
vector<int>vcc[100];
bool cut[100];
int read(){
    int sum=0,f=1;char x=getchar();
    while(x<'0'||x>'9'){
        if(x=='-') f=-1;
        x=getchar();
    }while(x>='0'&&x<='9'){
        sum=sum*10+x-'0';
        x=getchar();
    }return sum*f;
}
void add(int st,int ed){
    edge[++num].ed=ed;
    edge[num].nex=first[st];
    first[st]=num;
}
void addc(int st,int ed){
    edgec[++numc].ed=ed;
    edgec[numc].nex=firstc[st];
    firstc[st]=numc;
}
void tarjan(int x){
    dfn[x]=low[x]=++ord;
    sta[++top]=x;
    if(root==x&&first[x]==0){
        vcc[++vccnum].push_back(x);
        return ;
    }
    int child=0;
    for(int i=first[x];i;i=edge[i].nex){
        int y=edge[i].ed;
        if(!dfn){
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if(low[y]>=dfn[x]){
                child++;
                if((x==root&&child>=2)||(x!=root&&child>0)) cut[x]=1;
                vcc[++vccnum].push_back(x);
                int p;
                do{
                    p=sta[top--];
                    vcc[vccnum].push_back(p);
                }while(p!=y);
            }
        }else low[x]=min(low[x],dfn[y]);
    }
}
int main(){
    n=read();m=read();
    for(int i=1,x,y;i<=m;i++){
        x=read();y=read();
        add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i]) root,tarjan(i);
    nvcn=vccnum;
    for(int i=1;i<=n;i++)
        if(cut[i]) new_id[i]=++nvcn;
    for(int i=1;i<=vccnum;i++)
        for(int j=0;j<vcc[i].size();j++){
            int x=vcc[i][j];
            if(cut[x]){
                addc(new_id[x],i);
                addc(i,new_id[x]);
            }else bl[x]=i;
        }
    
}
View Code

以上代码均未测试,如有错误自行改正。

原文地址:https://www.cnblogs.com/Yu-shi/p/11183312.html