zoukankan      html  css  js  c++  java
  • 支配树

    何为支配树?

      定义:

        支配点:在一张图中起点为s,如果有一个点t,使得删去t后没有s到w的路径(不包含w本身),则称t为w的支配点
        最近支配点:记最近支配点为idom(w),下文简称iw。它是所有w的支配点中离w最近的(dfn值最大的,在dfs树中为w的祖先)

        半支配点:存在一条t到w的路径,使得路径中除了t和w,其他的点dfn值都比w的大,则t为w的半支配点。
        注:所有能直接到w的点都是半支配点
        最远半支配点:记最远半支配点为sdom(w),下文简称sw。它是所有w的半支配点中离w最远的(dfn值最小的)

        支配树即为一棵根为起点s,其他的点连向它的最近支配点的树。
        在原图上删去一个点后,起点和各个点的联通情况=在支配树中删去这个点,起点和各个点的联通情况
        (这里联通情况指单向的,及只表示起点是否能到各个节点)

    建造方法

      声明:

        路径P(u,v)表示沿着树边从u走到v中间遇到的点的集合(不包括u,v)。
        路径P[u,v]表示沿着树边从u走到v中间遇到的点的集合(包括u,v)。
        路径P(u,v],P[u,v)同理。

      一些性质(在图的dfs树上):

        1.sw为w的祖先
        证明:首先sw的dfn小于w的dfn,否则sdom(sw)也是w的半支配点并且dfn比sw要小。
        其次,假设sw不是w祖先且dfn[sw]<dfn[w],则一定是先搜sw,回溯后才继续搜索的w,但sw可以直接搜索到w,所以假设不成立

        2.iw为w的祖先
        证明:若iw不为w祖先,则可以直接从dfs树边到w且不经过iw。

        3.iw为sw或sw的祖先
        证明:假设不是,则iw为sw的后代,但sw可以有至少两条不相交的路径到w,iw只能在其中一条上

        4.对于w的祖先v,iw∈P[v,w]或iw∈P[r,iv]
        证明:假设不是,则iv到v至少有两条不相交的路径,iw只能在其中一条上,删掉iw从iv依然可以到v,即从s可以到w

      idom的确定定理

        1、对于w≠r,若∀u∈P(sw,w]均有sw≤su,则iw=sw 。

        证明:首先在sw下面没有支配w的点了(若sw支配w,则sw为iw)

        然后先说明sw支配了w。任取一条r到w的路径p,设x为路径中最低的不经过sw能到P(sw,w]的点(一定是dfs树中sw的祖先)。
        如果不存在这样的x,也就意味着sw一定是路径中的起点,显然有sw支配w。
         接着,令y为在P[sw,w]中第一个不受sw支配的点,再取q={x,v1⋯vk,y}(且不经过sw),那么有vi>y。
        (若vi<sw则x不是最低的不经过sw能到P(sw,w]的点,vi才是,若sw<vi<y,则y不是p在P[sw,w]中第一个不受sw支配的点,vi才是)
        这说明x是y的半支配点,蕴含着sdom(y)≤x,而x<sw,因此sdom(y)<sdom(w),由条件知不存在这样的x,y。
        这就说明了任何一条r到w路径都一定包含了sw 。

        2、对于w≠r​,取u∈P(sw,w]​中sdom​最小的u​,则su≤sw​且iw=iu​。

        证明:我们可以效仿上面的证明,任取一条r到w的路径p,设x为路径中最低的不经过iu能到P(iu,w]的点(一定是dfs树中sw的祖先)
        如果不存在则显然iu已经支配了w;令y为在P[iu,w]中第一个不受iu支配的点,再取q={x,v1⋯vk,y}(且不经过iu),那么有vi>y。
        这说明x是y的半支配点,蕴含着sdom(y)≤x,而x<sw,因此sdom(y)<sdom(w),由条件知不存在这样的x,y。所以iu支配w。
        然后取P(iu,w]中任何一点,显然都不支配w。所以iw=iu。

      确定sdom

        sdom(w)=min{{v∣(v,w)∈E且v<w}∪{sdom (u)∣u>w且存在边(v,w)使u为v或v的祖先}}(根据定义即可得到)

    代码实现

    #include<cstdio>
    #define maxn 100005
    #define maxm 500005
    struct Edge{
        int next,to;
    }edge[maxm],edge_pr[maxm],road[maxn];
    int n,m,fi[maxn],se,fi_pr[maxn],se_pr,dfn[maxn],si,a[maxn],fa[maxn],head[maxn],sr;
    int sdom[maxn],idom[maxn],myfind[maxn],mi_sdom[maxn],son[maxn];
    inline void add_edge(int u,int v){//存图的边 
        edge[++se].next=fi[u],edge[se].to=v,fi[u]=se;
    }
    inline void add_edge_pr(int u,int v){//存图的反向边 
        edge_pr[++se_pr].next=fi_pr[u],edge_pr[se_pr].to=v,fi_pr[u]=se_pr;
    }
    inline void add_road(int u,int v){//指向sdom为自己的点 
        road[++sr].next=head[u],road[sr].to=v,head[u]=sr;
    }
    void dfs(int x){//预处理出dfs树 
        a[dfn[x]=++si]=x;
        for(int i=fi[x];i;i=edge[i].next){
            int v=edge[i].to;
            if(!dfn[v])dfs(v),fa[v]=x;
        }
    }
    int Find(int x){//带权并查集路径压缩 
        if(x==myfind[x])return x;//dfn小于正在求的点,返回自己 
        int y=Find(myfind[x]);//返回祖先中dfn小于正在求的点的最深的点 
        if(dfn[sdom[mi_sdom[myfind[x]]]]<dfn[sdom[mi_sdom[x]]])mi_sdom[x]=mi_sdom[myfind[x]];//更新dfn大于正在求的点的最小的sdom 
        return myfind[x]=y;
    }
    inline void build(){
        for(int i=1;i<=n;i++)myfind[i]=sdom[i]=mi_sdom[i]=i;
        dfs(1);
        for(int j=si;j>1;j--){//必须按dfs序倒着算 
            int x=a[j];
            for(int i=fi_pr[x];i;i=edge_pr[i].next){//v为能到达x的点 
                int v=edge_pr[i].to;
                if(!dfn[v]) continue;
                Find(v);
                if(dfn[sdom[mi_sdom[v]]]<dfn[sdom[x]])sdom[x]=sdom[mi_sdom[v]];
                //若dfn[v]<dfn[x]则sdom[mi_sdom[v]]==v,否则sdom[mi_sdom[v]]为sdom (u)使得u为v的祖先,于是可求出sdom 
            }
            add_road(sdom[x],x);
            myfind[x]=fa[x],x=a[j-1];
            for(int i=head[x];i;i=road[i].next){
                int v=road[i].to;
                Find(v);//更新后mi_sdom为v到x路径中sdom最小的点 
                if(sdom[mi_sdom[v]]==x)idom[v]=x;//v到sdom[v]路径中没有sdom>x的点,则idom[v]=sdom[v]=x; 
                else idom[v]=mi_sdom[v];//否则idom[v]=idom[mi_sdom[v]](路径中sdom最小点的idom) 但idom[mi_sdom[v]]可能还没求出来,所以先记录一下 
            }
        }
        for(int i=2;i<=si;i++){
            int x=a[i];
            if(idom[x]!=sdom[x])idom[x]=idom[idom[x]];//将idom[v]=idom[mi_sdom[v]]没完成的步骤不上 
        }
    }
    int main(){
        int u,v;
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++){
            scanf("%d%d",&u,&v),add_edge(u,v),add_edge_pr(v,u);
        }
        build();//build后idom就求出来了,然后根据题目进行计算
        return 0;
    }

    参考文献:http://blog.csdn.net/GEOTCBRL/article/details/57875070

    http://blog.csdn.net/a710128/article/details/49913553

  • 相关阅读:
    Hi35XXX海思媒体处理平台架构介绍(转)
    STM32F103步进电机梯形匀加速算法(转)
    STM32+ESP8266通过AT指令WIFI连接阿里云MQTT服务器(转)
    ESP8266 WIFI串口通信模块使用详解(转)
    UCGUI字体研究(转)
    用STM32制作汽车蓝牙OBD转速/车速表(带LED转速指示)(转)
    用STM32制作汽车蓝牙OBD转速/车速表(带LED转速指示)(转)
    接口测试框架接入性能测试实践分享
    超全Python IDE武器库大总结,优缺点一目了然!
    推荐一款万能抓包神器:Fiddler Everywhere
  • 原文地址:https://www.cnblogs.com/bennettz/p/8003946.html
Copyright © 2011-2022 走看看