• 【学习笔记】支配树基础理论


    摘抄自 oi-wiki

    Part 1

    考虑在任意 有向图 上钦定一个入口节点 s s s,对于一个节点 u u u,若从 s s s u u u的每一条路径都经过某一个节点 v v v,那么称 v v v支配 u u u,记作 v dom ⁡ u v\operatorname{dom}u vdomu

    对于从 s s s出发无法到达的结点,讨论其支配关系是没有意义的,因此假设 s s s能到达图上任何一个节点

    引理1: s s s是所有节点的支配点;任意一个节点都是其自身的支配点

    引理2:仅考虑简单路径得出的支配关系与所有路径得出的关系相同

    引理3:如果 u dom ⁡ v u\operatorname{dom}v udomv v dom ⁡ w v\operatorname{dom}w vdomw,则 u dom ⁡ w u\operatorname{dom}w udomw

    引理4:如果 u dom ⁡ v , v dom ⁡ u u\operatorname{dom}v,v\operatorname{dom}u udomvvdomu,则 u = v u=v u=v

    引理5:若 u ≠ v ≠ w u\ne v\ne w u=v=w u dom ⁡ w u\operatorname{dom}w udomw v dom ⁡ w v\operatorname{dom}w vdomw,则有 u dom ⁡ v u\operatorname{dom}v udomv v dom ⁡ u v\operatorname{dom}u vdomu

    证明:考虑一条 s → u → v → w s\to u\to v\to w suvw的路径,若 u u u不支配 v v v,则存在一条 s → v → w s\to v\to w svw的路径,与 u dom ⁡ w u\operatorname{dom}w udomw矛盾(注意我们只考虑简单路径的情况)

    将任意节点 u u u的支配点中,除自身外与自己距离最近的节点 v v v称作 u u u的直接支配点,记作 idom ⁡ ( u ) = v \operatorname{idom}(u)=v idom(u)=v

    比较直观的理解:设 u u u的支配点集为 { s 1 , s 2 , . . . , s k } \{s_1,s_2,...,s_k\} {s1,s2,...,sk},则一定存在一条路径 s → s 1 → s 2 → . . . → s k → u s\to s_1\to s_2\to ...\to s_k\to u ss1s2...sku,显然 s k s_k sk就是 u u u的直接支配点。

    将除 s s s外每一个节点 u u u idom ⁡ ( u ) \operatorname{idom}(u) idom(u) u u u连边,我们就得到了一颗支配树

    结论:对于一个节点 u u u的支配点集 S S S,若 v ∈ S v\in S vS满足 ∀ w ∈ S   \   { u , v } , w dom ⁡ v \forall w\in S\ \backslash \ \{u,v\},w\operatorname{dom}v wS \ {u,v},wdomv,则 idom ⁡ ( u ) = v \operatorname{idom}(u)=v idom(u)=v(结合前面的理解不难证明,这个可能构造题会用到)

    Part 2

    对于 D A G DAG DAG,我们可以直接根据拓扑序求解。

    引理6:在有向图上, v dom ⁡ u v\operatorname{dom}u vdomu当且仅当 ∀ w ∈ p r e ( u ) , v dom ⁡ w \forall w\in pre(u),v\operatorname{dom} w wpre(u),vdomw

    因此, u u u的支配点一定是其所有前驱节点在支配树上的公共祖先。使用 L C A LCA LCA算法即可解决。

    Part 3

    求解任意有向图中支配树的方法:

    约定这里 w → v w\to v wv指的是存在 DFS 树上从 w w w v v v的路径。注意这个表述并不严谨,更严谨的记号可以参考 这篇博客。后文一些引理的证明也 参考了这篇博客

    首先从 s s s出发对有向图进行 DFS ,所经过的点和边形成了一棵树 T T T。令 d f n ( u ) dfn(u) dfn(u)表示节点 u u u第几个被遍历到;定义 u < v uu<v当且仅当 d f n ( u ) < d f n ( v ) dfn(u)dfn(u)<dfn(v)

    引理7(路径引理):如果两个点 v , w v,w v,w满足 v ≤ w v\le w vw,那么任意 v v v w w w的路径经过 v , w v,w v,w的公共祖先(不一定是 L C A LCA LCA

    证明主要是利用横叉边一定是从大的点到小的点。

    定义节点 u u u的半支配点为,从这个节点 v v v出发存在一条路径,路径上除了 u , v u,v u,v之外的每个节点都大于 u u u的结点中最小的那一个,记作 sdom ⁡ ( u ) \operatorname{sdom}(u) sdom(u)

    引理8:对于任意节点 u u u sdom(u) < u \text{sdom(u)}sdom(u)<u

    显然 f a ( u ) < u fa(u)fa(u)<u,并且是合法的半支配点。

    引理9:对于任意节点 u u u idom(u) \text{idom(u)} idom(u)是其在 T T T上的祖先

    引理10:对于任意节点 u u u sdom ( u ) \text{sdom}(u) sdom(u)是其在 T T T上的祖先

    如果 v v v不是 u u u的祖先,并且 v < u vv<u,那么 v v v u u u的路径一定经过 u , v u,v u,v的公共祖先(路径引理),不满足 v v v是支配点的条件。

    引理11:对于任意节点 u u u idom ( u ) \text{idom}(u) idom(u) sdom ( u ) \text{sdom}(u) sdom(u)的祖先

    引理12:对于任意节点 u ≠ v u\ne v u=v满足 v v v u u u的祖先,则要么有 v v v idom(u) \text{idom(u)} idom(u)的祖先,要么 idom(u) \text{idom(u)} idom(u) idom ( v ) \text{idom}(v) idom(v)的祖先

    证明:对于任意在 v v v idom ( v ) \text{idom}(v) idom(v)之间的节点 w w w,一定存在一条不经过 w w w的,从 s s s idom ( v ) \text{idom}(v) idom(v)再到 v v v的路径。因此这些节点一定不是 idom(u) \text{idom(u)} idom(u),因此 idom(u) \text{idom(u)} idom(u)要么是 v v v的后代,要么是 idom(v) \text{idom(v)} idom(v)的祖先

    引理13: sdom ( u ) = min ⁡ ( v ∣ ( v , u ) ∈ E , v < w ∪ sdom ( w ) ∣ w > u , ∃ ( v , u ) ∈ E , w → v ) \text{sdom}(u)=\min(v|(v,u)\in E,vu,\exist (v,u)\in E,w\to v) sdom(u)=min(v(v,u)E,v<wsdom(w)w>u,(v,u)E,wv)

    可以从大到小枚举点,用带权并查集维护,从而求得所有点的半支配点。

    引理14:对于任意 u ≠ r u\ne r u=r,如果所有 sdom(u) \text{sdom(u)} sdom(u) u u u之间(不包括 sdom(u) \text{sdom(u)} sdom(u))的点 v v v满足 sdom(v) ≥ sdom(u) \text{sdom(v)}\ge \text{sdom(u)} sdom(v)sdom(u),那么 idom(u) = sdom(u) \text{idom(u)}=\text{sdom(u)} idom(u)=sdom(u)

    证明:只需证明 sdom(u) \text{sdom(u)} sdom(u)支配 u u u即可。考虑任意 s s s w w w的路径,取上面最后一个编号小于 sdom(u) \text{sdom(u)} sdom(u) x x x,则一定存在 y y y满足 y y y sdom(u) \text{sdom(u)} sdom(u) u u u之间。同理,考虑取最小的的 y y y,如果 y y y不是 sdom(u) \text{sdom(u)} sdom(u),那么根据条件, sdom(y) ≥ sdom(u) \text{sdom(y)}\ge \text{sdom(u)} sdom(y)sdom(u),所以 x x x不可能是 sdom(y) \text{sdom(y)} sdom(y),因此从 x x x y y y的路径上也存在一个 sdom(y) \text{sdom(y)} sdom(y) y y y之间的节点 w w w(这与之前的推理是一样的),这与 y y y最小矛盾。

    这个结论比较重要,能让我们比较直观的感受到半支配点的作用。

    引理15:对于任意 u u u,令 v v v sdom(u) \text{sdom(u)} sdom(u) u u u之间(不包括 sdom(u) \text{sdom(u)} sdom(u) sdom(v) \text{sdom(v)} sdom(v)最小的一个,并且 sdom(v) ≤ sdom(u) \text{sdom(v)}\le \text{sdom(u)} sdom(v)sdom(u),那么 idom(u) = idom(v) \text{idom(u)}=\text{idom(v)} idom(u)=idom(v)

    证明:条件可以写成 sdom(v) → sdom(u) → v → u \text{sdom(v)}\to \text{sdom(u)}\to v\to u sdom(v)sdom(u)vu

    由引理12可知 idom(u) → idom(v) \text{idom(u)}\to \text{idom(v)} idom(u)idom(v) v → idom(u) v\to \text{idom(u)} vidom(u),排除后面那种(引理11),只需证明 idom(v) \text{idom(v)} idom(v)支配 u u u。这是比较显然的。

    引理16:对于任意 u u u,令 v v v sdom ( u ) \text{sdom}(u) sdom(u) u u u之间(不包括 sdom(u) \text{sdom(u)} sdom(u) sdom ( v ) \text{sdom}(v) sdom(v)最小的一个,则:

    idom(u) = { sdom(u) sdom(v) = sdom(u) idom(v) sdom(v) < sdom(u) \text{idom(u)}=

    {sdom(u)sdom(v)=sdom(u)idom(v)sdom(v)<sdom(u)" role="presentation" style="position: relative;">{sdom(u)sdom(v)=sdom(u)idom(v)sdom(v)<sdom(u)
    idom(u)={sdom(u)idom(v)sdom(v)=sdom(u)sdom(v)<sdom(u)

    说白了就是前两个引理。

    可以发现求 v v v的过程和求半支配点是类似的,因此当从大到小处理到 sdom(u) \text{sdom(u)} sdom(u)的时候就可以顺便求出 u u u对应的信息。最后再从小到大遍历一遍即可。

    复杂度 O ( n + m ) O(n+m) O(n+m)

    吐槽:oi-wiki上的写法是错的,会被叉。网上的代码就没几个靠谱的。。。

    #include
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define inf 0x3f3f3f3f
    using namespace std;
    const int N=2e5+5;
    int n,m,mn[N],dfn[N],num,ps[N],idm[N],sdm[N],fa[N],fth[N],sz[N];
    vector<int>G[N],G2[N],G3[N],G4[N];
    void dfs(int u){
        dfn[u]=++num,ps[num]=u;
        for(auto v:G[u]){
            if(!dfn[v])fth[v]=u,dfs(v);
        }
    }
    int find(int x){
        if(fa[x]==x)return x;
        int tmp=fa[x];
        fa[x]=find(fa[x]);
        if(dfn[sdm[mn[tmp]]]<dfn[sdm[mn[x]]]){
            mn[x]=mn[tmp];
        }return fa[x];
    }
    void solve(){
        dfs(1);
        for(int i=1;i<=n;i++)fa[i]=sdm[i]=mn[i]=i,idm[i]=1;
        for(int i=num;i>=2;i--){
            int u=ps[i],res=inf;
            for(auto v:G2[u]){
                if(dfn[v]<dfn[u]){
                    res=min(res,dfn[v]);
                }
                else{
                    find(v);
                    res=min(res,dfn[sdm[mn[v]]]);
                }
            }
            //u=fth[u];
            for(auto v:G3[u]){
                find(v);
                if(sdm[mn[v]]==sdm[v]){
                    idm[v]=u;
                }
                else{
                    idm[v]=mn[v];
                }
            }
            sdm[u]=ps[res];
            G3[sdm[u]].pb(u);
            fa[u]=fth[u];
        }
        for(int i=2;i<=num;i++){
            int u=ps[i];
            if(idm[u]!=sdm[u]){
                idm[u]=idm[idm[u]];
            }
        }
    }
    void build(){
        for(int i=2;i<=n;i++)G4[idm[i]].pb(i);
    }
    void dfs2(int u){
        sz[u]=1;
        for(auto v:G4[u])dfs2(v),sz[u]+=sz[v];
    }
    int main(){
        ios::sync_with_stdio(false);
        cin.tie(0),cout.tie(0);
        cin>>n>>m;
        for(int i=1;i<=m;i++){
            int x,y;cin>>x>>y;
            G[x].pb(y),G2[y].pb(x);
        }
        solve();
        build();
        dfs2(1);
        for(int i=1;i<=n;i++)cout<<sz[i]<<" ";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
  • 相关阅读:
    UE4 第一人称角色模板 添加蹲伏功能
    Milvus 2.3.功能全面升级,核心组件再升级,超低延迟、高准确度、MMap一触开启数据处理量翻倍、支持GPU使用!
    拼接字符串,得到字典序最小的结果
    数学建模美赛入门
    C#应用程序界面开发基础——窗体控制(1)——Form窗体(删除事件部分,没看懂)
    Linux基本指令(下)
    Vue3.2中,CSS与数据的双向绑定
    高教社杯数模竞赛特辑论文篇-2023年E题:基于小波变换和背包模型的小浪底水库最优监测方案研究(附获奖论文及Python代码实现)
    网络原理之封装和分用,网络编程套接字
    如何搭建游戏平台?
  • 原文地址:https://blog.csdn.net/cqbzlydd/article/details/134084166