tarjan进阶

Posted yu-shi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

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

以上是关于tarjan进阶的主要内容,如果未能解决你的问题,请参考以下文章

tarjan进阶

Tarjan算法总结

tarjan 算法与图的连通性

Tarjan之求LCA

tarjan缩点与割点

tarjan讲解(用codevs1332(tarjan的裸题)讲解)