zoukankan      html  css  js  c++  java
  • [学习笔记]Tarjan&&欧拉回路

    本篇并不适合初学者阅读。

    SCC:

    1.Tarjan缩点:x回溯前,dfn[x]==low[x]则缩点。

    注意:

    ①sta,in[]标记。

    ②缩点之后连边可能有重边。

    2.应用:

    SCC应用范围还是很广的。

    基本思路是:Tarjan+topo

    DAG是个好东西。

    各种判断连通性(传递关系),计数,以及dp转移

    感觉90%以上的tarjan都是考SCC

    例题:最大半连通子图(这个题注意缩点后的重边)

    衍生应用算法:

    ①2-SAT

    ②完备匹配的二分图必须边和可行边。

    E-DCC

    1.Tarjan找桥:x的一个子节点y,dfn[x]<low[y],边就是桥

    注意:

    ①in_edge,对称建边号。不能从in_edge^1出发。但是可以走重边。

    然后不经过桥dfs,找E-DCC

    2.应用:

    ①最短路的必经边:建出最短路图,找桥即可。

    ②例题:poj3694

    找出桥边,E-DCC缩点。

    对于询问(x,y)x,y不在一个DCC,

    找LCA,之后可以往上暴力找路径,干掉桥。

    然鹅使用并查集更快。

    O(M+N+Qlogn)

    V-DCC

    1.找割点:dfn[x]<=low[y]不用管重边什么的。

    注意:

    ①搜索树根节点要有两次符合,才是割点。

    其实对于割点x,dfn[x]<=low[y]的出点y,这些y一定在不同的DCC中。

    缩点:

    除了孤立点之外,每个DCC大小至少为2


    每个割点属于多个DCC

    用栈维护。

    如果某个点满足dfn[x]<=low[y]

    那么,不断弹出栈顶,直到y弹出。

    把这些点和x都放进一个DCC中。(vector存)

    可以发现一个x属于多个DCC

    缩点的时候,

    先把割点设置编号(cnt+1~cnt+tot)cnt是DCC个数

    tot是割点个数

    循环所有的DCC,循环所有的成员

    如果找到割点,就让这个割点和这个DCC连边。

    连出一个黑点白点相间的树(黑点可以认为是割点)

    例题:

    [HNOI2012]矿场搭建

    分类讨论即可

    KNIGHTS - Knights of the Round Table

    性质题,

    建立反图。没有仇恨的人连边。

    没有出现在任何一个简单奇环中的骑士滚蛋

    V-DCC缩点

    如果两个骑士DCC不同,一定不能同时出席。(否则在一个奇环中的话,那么两个DCC可以合并。矛盾)

    如果一个DCC中有奇环,那么DCC中的所有骑士都可以被一个奇环包含。(可以构造)

    综上,一个骑士被包含,因为不能和其他DCC合作,所以必须当且仅当自己的DCC中有奇环

    Tarjan+二分图染色判断奇环

    代码:

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    namespace Miracle{
    const int N=1005;
    const int M=1e6+5;
    bool hate[N][N];
    int n,m;
    void rd(int &x){
        char ch;x=0;
        while(!isdigit(ch=getchar()));
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    }
    struct node{
        int nxt,to;
    }e[2*M];
    int cnt,hd[N];
    void add(int x,int y){
        e[++cnt].nxt=hd[x];
        e[cnt].to=y;
        hd[x]=cnt;
    }
    bool cut[N];
    bool has[N];//for cut
    vector<int>mem[N];
    int rt;
    int be[N];
    int sta[N],top;
    int dfn[N],low[N];
    int df;
    int dcc;
    void tarjan(int x){
        sta[++top]=x;
        low[x]=dfn[x]=++df;
        bool fl=false;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
                if(dfn[x]<=low[y]){
                    ++dcc;
                    mem[dcc].clear();
                    if(fl||x!=rt) cut[x]=1;
                    fl=true;
                    int z;
                    do{
                        z=sta[top--];
                        be[z]=dcc;
                        mem[dcc].push_back(z);
                    }while(z!=y);
                    be[x]=dcc;
                    mem[dcc].push_back(x);
                }
            }
            else low[x]=min(low[x],dfn[y]);
        }
    }
    int c[N];
    bool bla;
    bool ok[N];
    void che(int x,int las,int id){
    //    cout<<" che "<<x<<endl;
        c[x]=las;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            
            if(be[y]!=id) continue;
        //    cout<<" goto "<<y<<endl;
            if(!c[y]){
                che(y,3-las,id);
            }
            else if(c[y]==c[x]) bla=false;
            //if(!fl) return false;
        }
        //return fl;
    }
    void clear(){
        memset(hate,0,sizeof hate);
        memset(cut,0,sizeof cut);
        memset(hd,0,sizeof hd);cnt=0;
        memset(dfn,0,sizeof dfn);df=0;
        memset(ok,0,sizeof ok);
        dcc=0;
        memset(c,0,sizeof c);
        memset(has,0,sizeof has);
        memset(be,0,sizeof be);
    }
    int main(){
        while(1)
        {
            scanf("%d%d",&n,&m);int x,y;
            if(n==0&&m==0) break;
            clear();
            for(reg i=1;i<=m;++i){
                rd(x);rd(y);
                hate[x][y]=1;
            }
            for(reg i=1;i<=n;++i){
                for(reg j=i+1;j<=n;++j){
                    if(!hate[i][j]){
                        add(i,j);add(j,i);
                    }
                }
            }
            for(reg i=1;i<=n;++i){
                if(!dfn[i]) rt=i,top=0,tarjan(i);
            }
    //        cout<<" dcc "<<dcc<<endl;
    //        for(int i=1;i<=n;++i){
    //            cout<<i<<" : "<<cut[i]<<" "<<be[i]<<endl;
    //        }cout<<endl;
            for(reg i=1;i<=dcc;++i){
            //    cout<<" i---------- "<<i<<endl;
                memset(c,0,sizeof c);
                for(reg j=0;j<mem[i].size();++j){
            //        cout<<" mem "<<mem[i][j]<<endl;
                        be[mem[i][j]]=i;
                }
                bla=true;
                che(mem[i][0],1,i);
            //    cout<<" bla "<<bla<<endl;
                if(!bla){
                    for(reg j=0;j<mem[i].size();++j){
                        ok[mem[i][j]]=1;
                    }
                }
            }
            
            int ans=n;
            for(reg i=1;i<=n;++i){
                ans-=ok[i];
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    
    }
    int main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/11/6 7:40:18
    */
    View Code

    附赠福利:

    欧拉回路:

    void dfs(int x){

    for(each son)
    if(!vis[i]){

    vis[i]=vis[i^1]=1;

    dfs(e[i].to)
    }

    sta[++top]=x;

    }

    然后sta倒着输出即可。

    至于每个点访问多次,可以弧优化减少枚举。

  • 相关阅读:
    maven项目自动创建src/main/resources等四个资源文件夹
    @Async异步注解与SpringBoot结合使用
    java.util.concurrent包下并发锁的特点与适用场景
    java.util.concurrent包下集合类的特点与适用场景
    springMvc接收ajax数组参数,以及jquery复选框选中、反选、全选、全不选
    MySql 的SQL执行计划查看,判断是否走索引
    windows server系统查看tomcat版本
    物化视图SQL
    element-ui的不稳定性
    ActiveMQ-5.15.2下载和启动(windows)
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9911695.html
Copyright © 2011-2022 走看看