zoukankan      html  css  js  c++  java
  • 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

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

  • 相关阅读:
    翻译:Razor剖析之第4部分:Razor页面
    学习第二十二天
    jQuery:选择器和事件
    学习第二十天@简单json+上传文件+Ado存储过程
    统计指定时间段内的周未(非周未)天数
    c#动态创建内存模型(笔记)
    cmd命令 任务计划 详解
    BAT教程:第四节(批处理中的变量)
    103个Windows XP运行命令
    BAT教程 :第二节(for命令详解 )
  • 原文地址:https://www.cnblogs.com/Yu-shi/p/11183312.html
Copyright © 2011-2022 走看看