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

摘抄自 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 u<v u<v当且仅当 d f n ( u ) < d f n ( v ) dfn(u)<dfn(v) 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)}<u sdom(u)<u

显然 f a ( u ) < u fa(u)<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 v<u v<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,v<w\cup \text{sdom}(w)|w>u,\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)}= \begin{cases} \text{sdom(u)}& \text{sdom(v)}=\text{sdom(u)} \\ \text{idom(v)}&\text{sdom(v)}<\text{sdom(u)} \end{cases} 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<bits/stdc++.h>
#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]<<" ";
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值