zoukankan      html  css  js  c++  java
  • Tarjan系列1

    tajan的dfs树系列算法:

    求解割点,桥,强连通分量,点双联通分量,边双联通分量;

    tajan是一个dfs,把一个图变成一个dfs树结构,

    dfs树结构,本质是通过一个没有任何要求的dfs把图的边分为:树边和返祖边:

    • 树边:dfs中父节点与其未曾遍历过的子节点间的边,
    • 返祖边:父节点与他的dfs中曾作为该父节点祖先的子节点间的边

    在有向图中,除了这二种边外,还有父节点与曾遍历过的子节点间的边,然而这个子节点不是父节点的祖先,

    然而这种边在tarjan中没有意义,我们所求的东西用不上她们

    伪代码:

    深搜(点now){

      更新点now——

        dfs序(dfn)与目前可到dfn最小祖先的dfn(low),标记已经遍历(vis),确认now将是他后继递归的点的祖先(instk),其他

      for(以now为起点的所有边)

        if(边终点to未遍历)

          深搜(to),low[now]=min(low[now],low[to])

        else

          if(instk[to]为真)//无向图可以不存在这个

            low[now]=min(low[now],dfn[to])

      更新instk[now]为假

    }

    求(无向图)割点,桥:

    割点:无向图中,删除之可改变图的连通性的点

      两种:

    • 两个点双连通分量的公共点
    • 两个点双连通分量直接存在一条边连接,这条边的两端点

    桥:无向图中,删除之可改变图的连通性的边

      一种:

      两个边双连通分量间的连边;

    求法:

    割点:这个点儿子中有至少一个的low小于这个点的dfn

    桥:该边终点的low=dfn

    例题:

    hihocoder1183连通性一·割边与割点

    code:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n,m;
    struct ss{
        int to,next;
    }e[200010];
    struct Cl{
        int u,v;
    }Cedge[200010];
    int first[20010],num;
    int pnu,enu;
    int Cpoint[20010];
    int dfn[20010],low[20010],vis[20010];
    bool cmp(Cl a,Cl b){
        return a.u<b.u||(a.u==b.u&&a.v<b.v);
    }
    void Input();
    void work();
    void Output();
    void build(int ,int );
    void Init();
    void dfs_1(int ,int );
    void dfs_2(int ,int );
    int main()
    {
        Input();
        work();
        Output();
        return 0;
    }
    void Input(){
        int i,j,k;
        scanf("%d%d",&n,&m);
        for(i=1;i<=m;i++){
            scanf("%d%d",&j,&k);
            build(j,k);build(k,j);
        }
    }
    void work(){
        pnu=0;enu=0;
        Init();
        dfs_1(1,0);
        Init();
        dfs_2(1,0);
    }
    void Output(){
        int i;
        for(i=1;i<=enu;i++)
            if(Cedge[i].u>Cedge[i].v)
                swap(Cedge[i].u,Cedge[i].v);
        sort(Cpoint+1,Cpoint+pnu+1);
        sort(Cedge+1,Cedge+enu+1,cmp);
        for(i=1;i<=pnu;i++)
            printf("%d ",Cpoint[i]);
        if(pnu==0)
            printf("Null");
        printf("
    ");
        for(i=1;i<=enu;i++)
            printf("%d %d
    ",Cedge[i].u,Cedge[i].v);
    }
    void build(int f,int t){
        e[++num].next=first[f];
        e[num].to=t;
        first[f]=num;
    }
    void Init(){
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(vis,0,sizeof(vis));
        num=0;
    }
    void dfs_1(int now,int fa){
        int i,p=0;
        if(now!=1)p=1;
        dfn[now]=low[now]=++num;vis[now]=1;
        for(i=first[now];i;i=e[i].next)
            if(e[i].to!=fa){
                if(!vis[e[i].to]){
                    dfs_1(e[i].to,now);
                    if(low[e[i].to]>=dfn[now])p++;
                    if(low[now]>low[e[i].to])
                        low[now]=low[e[i].to];
                }
                else
                    if(low[now]>dfn[e[i].to])
                        low[now]=dfn[e[i].to];
            }
        if(p>=2)
            Cpoint[++pnu]=now;
    }
    void dfs_2(int now,int fa){
        int i;
        dfn[now]=low[now]=++num;vis[now]=1;
        for(i=first[now];i;i=e[i].next)
            if(e[i].to!=fa){
                if(!vis[e[i].to]){
                    dfs_2(e[i].to,now);
                    if(low[now]>low[e[i].to])
                        low[now]=low[e[i].to];
                    if(low[e[i].to]>dfn[now])
                        Cedge[++enu].u=now,Cedge[enu].v=e[i].to;
                }
                else
                    if(low[now]>dfn[e[i].to])
                        low[now]=dfn[e[i].to];
            }
    }
    View Code

    求(有向图)强连通分量:

    强联通分量:有向图中,点的可以相互到达的关系可以传递,于是有这个关系的一组点与其间的边构成一个强连通分量

    求法:

    每遍历一个点时,使之进栈,

    当遍历结束时,

    若其low=dfn,则从栈顶到该节点的所有点属于同一分量,且该分量不含其它点,标记她们,并使她们出栈

    (now永远不能到达上层的点,于是她与她的子树中没有自成一派的点构成强连通分量,自成一派的点已经出栈了)

    例题:

    bzoj1051[HAOI2006]受欢迎的牛

    强连通分量缩点后讨论无出度点的个数

    code:

    #include<cstdio>
    using namespace std;
    struct ss{
        int to,next;
    }e[2000010];
    int first[200010],num;
    int dfn[100010],low[100010],vis[200010],stk[100010],col[100010],number;
    int numcol[200010],into[100],color;
    int max[200010];
    long long f[200010],x;
    int n,m;
    void Input();
    void work();
    void Output();
    void build(int ,int );
    void tar(int );
    void con_poi();
    void dfs(int );
    int main()
    {
        Input();
        work();
        Output();
        return 0;
    }
    void Input(){
        int i,j,k;
        scanf("%d%d%lld",&n,&m,&x);
        for(i=1;i<=m;i++){
            scanf("%d%d",&j,&k);
            build(j,k);
        }
    }
    void work(){
        int i,j;
        color=n;
        for(i=1;i<=n;i++)
            if(!vis[i])
                number=0,tar(i);
        con_poi();
        for(i=n+1;i<=color;i++)
            if(!vis[i])
                dfs(i);
    }
    void Output(){
        int i;
        long long ans=0,ans_=0;
        for(i=n+1;i<=color;i++){
            if(max[i]==ans)
                (ans_+=f[i])%=x;
            if(max[i]>ans)
                ans=max[i],ans_=f[i];
        }
        printf("%lld
    %lld
    ",ans,ans_);
    }
    void build(int f,int t){
        e[++num].next=first[f];
        e[num].to=t;
        first[f]=num;
    }
    void tar(int now){
        int i;
        dfn[now]=low[now]=++number;
        vis[now]=2;stk[++stk[0]]=now;
        for(i=first[now];i;i=e[i].next)
            if(!vis[e[i].to]){
                tar(e[i].to);
                if(low[now]>low[e[i].to])
                    low[now]=low[e[i].to];
            }
            else
                if(vis[e[i].to]==2&&low[now]>dfn[e[i].to])
                    low[now]=dfn[e[i].to];
        if(dfn[now]==low[now]){
            ++color;
            while(stk[stk[0]+1]!=now){
                col[stk[stk[0]]]=color;
                vis[stk[stk[0]]]=1;
                ++numcol[color];
                --stk[0];
            }
            number=stk[0];
        }
    }
    void con_poi(){
        int i,j;
        for(i=1;i<=n;i++)
            for(j=first[i];j;j=e[j].next)
                if(col[i]!=col[e[j].to])
                    build(col[i],col[e[j].to]),into[col[e[j].to]]++;
    }
    void dfs(int now){
        int i;
        f[now]=1;max[now]=numcol[now];
        for(i=first[now];i;i=e[i].next)
            if(!vis[e[i].to]){
                vis[e[i].to]=1;
                if(!f[e[i].to])
                    dfs(e[i].to);
                if(max[now]==max[e[i].to]+numcol[now])
                    (f[now]+=f[e[i].to])%=x;
                if(max[now]<max[e[i].to]+numcol[now]){
                    max[now]=max[e[i].to]+numcol[now];
                    f[now]=f[e[i].to];
                }
            }
        for(i=first[now];i;i=e[i].next)
            if(vis[e[i].to])
                vis[e[i].to]=0;
    }
    View Code

    bzoj1093[ZJOI2007]最大半连通子图

    强连通分量缩点,计算最长路和最长路计数

    code:

    #include<cstdio>
    using namespace std;
    struct ss{
        int to,next;
    }e[2000010];
    int first[200010],num;
    int dfn[100010],low[100010],vis[200010],stk[100010],col[100010],number;
    int numcol[200010],into[100],color;
    int max[200010];
    long long f[200010],x;
    int n,m;
    void Input();
    void work();
    void Output();
    void build(int ,int );
    void tar(int );
    void con_poi();
    void dfs(int );
    int main()
    {
        Input();
        work();
        Output();
        return 0;
    }
    void Input(){
        int i,j,k;
        scanf("%d%d%lld",&n,&m,&x);
        for(i=1;i<=m;i++){
            scanf("%d%d",&j,&k);
            build(j,k);
        }
    }
    void work(){
        int i,j;
        color=n;
        for(i=1;i<=n;i++)
            if(!vis[i])
                number=0,tar(i);
        con_poi();
        for(i=n+1;i<=color;i++)
            if(!vis[i])
                dfs(i);
    }
    void Output(){
        int i;
        long long ans=0,ans_=0;
        for(i=n+1;i<=color;i++){
            if(max[i]==ans)
                ans_+=f[i];
            if(max[i]>ans)
                ans=max[i],ans_=f[i];
        }
        printf("%lld
    %lld
    ",ans,ans_);
    }
    void build(int f,int t){
        e[++num].next=first[f];
        e[num].to=t;
        first[f]=num;
    }
    void tar(int now){
        int i;
        dfn[now]=low[now]=++number;
        vis[now]=2;stk[++stk[0]]=now;
        for(i=first[now];i;i=e[i].next)
            if(!vis[e[i].to]){
                tar(e[i].to);
                if(low[now]>low[e[i].to])
                    low[now]=low[e[i].to];
            }
            else
                if(vis[e[i].to]==2&&low[now]>dfn[e[i].to])
                    low[now]=dfn[e[i].to];
        if(dfn[now]==low[now]){
            ++color;
            while(stk[stk[0]+1]!=now){
                col[stk[stk[0]]]=color;
                vis[stk[stk[0]]]=1;
                ++numcol[color];
                --stk[0];
            }
            number=stk[0];
        }
    }
    void con_poi(){
        int i,j;
        for(i=1;i<=n;i++)
            for(j=first[i];j;j=e[j].next)
                if(col[i]!=col[e[j].to])
                    build(col[i],col[e[j].to]),into[col[e[j].to]]++;
    }
    void dfs(int now){
        int i;
        f[now]=1;max[now]=numcol[now];
        for(i=first[now];i;i=e[i].next)
            if(!vis[e[i].to]){
                vis[e[i].to]=1;
                if(!f[e[i].to])
                    dfs(e[i].to);
                if(max[now]==max[e[i].to]+numcol[now])
                    (f[now]+=f[e[i].to])%=x;
                if(max[now]<max[e[i].to]+numcol[now]){
                    max[now]=max[e[i].to]+numcol[now];
                    f[now]=f[e[i].to];
                }
            }
        for(i=first[now];i;i=e[i].next)
            if(vis[e[i].to])
                vis[e[i].to]=0;
    }
    View Code

    bzoj2438[中山市选2011]杀人游戏

    强连通分量缩点,乱搞,具体看代码;

    code:

    #include<cstdio>
    #include<cstring>
    using namespace std;
    int n,m,p,ans;
    struct ss{
        int to,next;
    }e[600010];
    int first[200010],num;
    int dfn[100010],low[100010],vis[200010],stk[200010],number;
    int col[100010],color,numcol[200010];
    int toit[200010];
    void Input();
    void work();
    void Output();
    void build(int ,int );
    void tar(int );
    void dfs_1(int );
    int main()
    {
        Input();
        work();
        Output();
        return 0;
    }
    void Input(){
        int i,j,k;
        scanf("%d%d",&n,&m);
        for(i=1;i<=m;i++){
            scanf("%d%d",&j,&k);
            build(j,k);
        }
    }
    void work(){
        int i,j,k;
        color=n;
        for(i=1;i<=n;i++)
            if(!vis[i])
                number=0,tar(i);
        for(i=1;i<=n;i++)
            for(j=first[i];j;j=e[j].next)
                if(col[i]!=col[e[j].to])
                    build(col[i],col[e[j].to]),toit[col[e[j].to]]++;
        memset(stk,0,sizeof(stk));
        for(i=n+1;i<=color;i++)
            if(!toit[i])
                vis[i]++,dfs_1(i);
        for(i=n+1;i<=color;i++)
            if(!toit[i]){
                k=(numcol[i]==1);
                for(j=first[i];j;j=e[j].next)
                    k&=(vis[e[j].to]>1);
                p|=k;
                ans++;
            }
    }
    void Output(){
        printf("%.6lf",1.0-(double)(ans-p)/(double)(n));
    }
    void build(int f,int t){
        e[++num].next=first[f];
        e[num].to=t;
        first[f]=num;
    }
    void tar(int now){
        int i;
        dfn[now]=low[now]=++number;
        stk[++stk[0]]=now;vis[now]=2;
        for(i=first[now];i;i=e[i].next)
            if(!vis[e[i].to]){
                tar(e[i].to);
                if(low[now]>low[e[i].to])
                    low[now]=low[e[i].to];
            }
            else
                if(vis[e[i].to]==2&&low[now]>dfn[e[i].to])
                    low[now]=dfn[e[i].to];
        if(dfn[now]==low[now]){
            ++color;
            while(stk[stk[0]+1]!=now){
                col[stk[stk[0]]]=color;
                vis[stk[stk[0]]]=1;
                ++numcol[color];
                --stk[0];
            }
            number=stk[0];
        }
    }
    void dfs_1(int now){
        int i;
        for(i=first[now];i;i=e[i].next)
            if(!stk[e[i].to]){
                stk[e[i].to]=1;
                vis[e[i].to]++;
                if(vis[e[i].to]==1)
                    dfs_1(e[i].to);
            }
        for(i=first[now];i;i=e[i].next)
            if(stk[e[i].to])
                stk[e[i].to]=0;
    }
    View Code

    边双点双下次再说吧;

  • 相关阅读:
    yii2框架随笔27
    yii2框架随笔26
    yii2框架随笔25
    yii2框架随笔24
    yii2框架随笔23
    yii2框架随笔21
    yii2框架随笔20
    yii2框架随笔19
    yii2源码学习笔记(十五)
    yii2源码学习笔记(十四)
  • 原文地址:https://www.cnblogs.com/nietzsche-oier/p/7522401.html
Copyright © 2011-2022 走看看