支配树(Dominator Tree)学习笔记

因为是学习笔记所以是边学边写,以防自己到时候忘掉了=w=

一些定义和性质

首先,支配树是一棵树(废话),用来求解必经点问题。
即求一个有向图中以r为根到达每一个点的必经点。
树中每一个点x的父亲为idom(x),表示x的最近必经点。
我们把从r到x必经y称作“y支配x”,显然支配是具有传递性的。
所以可以证明idom是唯一的。

首先对原图G建出其的dfs树T并且按dfs序重标号,那么就具有一些性质:

1:横叉边必然从大指向小
这就表明对于任意两点u,v,u<=v,u到v中的任意路径都经过u,v在T中的一个公共祖先

2:idom(x)必为x在T中的祖先
废话

定义半必经点sdom(x),表示最小的v,使得存在一条从v到x的路径不经过任何树边。
3:sdom(x)必为x在T中的祖先
因为sdom(x)<=x,根据性质1可知sdom(x)到x的那条路径必然经过x和sdom(x)的一个公共祖先。而这个公共祖先就是sdom(x)。如果不是就证明sdom(x)是通过横叉边走到x的祖先中,这显然是不合法的。

4:idom(x)必为sdom(x)在T中的祖先
二者都是x的祖先,并且idom(x)不可能为sdom(x)的后代,否则将被完全跳过。

5:如果v是x的祖先,那么v是idom(x)的祖先或者idom(x)是idom(v)的祖先。
如果v是idom(x)的后代,并且idom(v)是idom(x)的祖先,那么就可以从r绕过idom(x)走到v,再直接走到x,与已知矛盾。

求解Sdom

接下来讲如何求sdom(x)
先给出结论,对于x的一个前继y,若y < x,sdom(x)=min(y)
否则sdom(x)=min(sdom(z)),z是y的祖先且z > x

证明:设右边的数为v,先证明sdom(x)<=v,再证明sdom(x)>=v
1”,对于第一种显然成立
第二种,存在一条从sdom(z)到z到y到x的路径
其中sdom(z)到z这一段除头尾均>z,即>x
后一段显然
所以证明了sdom(x)<=v
2”如果sdom(x)只经过一条边,必有sdom(x)>=v,因为v<=min(y),y是x的前继
否则我们设路径v0=sdom(x),v1,v2,v3…vk=x,vj是首个满足vj是vk-1的父亲的。

可以证明v1…vj-1>vj,否则我们取最小的vi满足vi < vj,必然满足vi到vj的路径上经过vi和vj的一个祖先,设为w,可以通过类似性质3的证明发现w就是vi,那么vi是vj的祖先,也就是vi是vk-1的祖先,与假设矛盾。

这其实就是构造了一个集合满足第二种条件,vj相当于z,于是sdom(x)=sdom(vj)>=v,因为我们的路径是任取的,并且对于所有的路径都满足条件。
综上sdom(x)=v

求解idom

说了这么多我们就是要用sdom来求idom
其实我们发现如果我们将所有的边 {(u,v)|(u,v)⊄T} 删去,然后将所有的 {(sdom(x),x),xr} 加上,我们会发现每个点的idom并不会改变,于是我们直接用log的DAG做法就好了。
具体来说idom(x)=LCA(idom(y)),y是x的前继。
但是tarjan提出了一个叫做Lengauer-Tarjan的利用并查集达到O(nα(n))的做法

让我们再来讲几个用sdom求idom的定理:
1:若所有在sdom(x)到x的路径上的点y,y!=sdom(x)都满足sdom(y)>=sdom(x)则idom(x)=sdom(x)
证明:对于任意一条r到x的路径,设点z为最后一个小于sdom(x)的点。
若没有则显然sdom(x)支配x
设w为路径上z之后首个在sdom(x)到x路径上的点(不含端点)
类似sdom的证明,路径上z到w的点vi>w
所以sdom(w)<=z < sdom(x)
又因为条件知道sdom(w)>=sdom(x),所以w不能是sdom(x)的后代,与假设矛盾。

2:令u是sdom(x)到x的路径上的sdom(u)最小的点,u!=sdom(x)
则idom(x)=idom(u)
证明:类似上一个的证明形式,取最后一个点z满足z < idom(u)
再取第一个y满足y在idom(u)到x的路径上。
同理可得sdom(y)<=z,那么我们知道sdom(y)<=z < idom(u)<=sdom(u)<=sdom(x) < u<=x
现在我们来尝试确定y的位置。
首先y一定不在sdom(x)到x的路径上,如果在就违反了sdom(u)最小
其次y一定不再idom(u)到sdom(x)的路径上,如果在我们就存在一条从r到sdom(y)到y避开idom(u)到u的路径。
这样就说明了y=idom(u),即idom(u)支配x

也就是说,我们取从sdom(x)到x路径上sdom(u)最小的u,如果u=x,那么idom(x)=sdom(x),否则idom(x)=idom(u)

Lengauer-Tarjan

首先考虑求sdom的做法,我们只需要求出对于点x所有大于他的祖先。
考虑按dfs序倒序处理,用带权并查集维护,很容易处理出sdom最小的那个点。
对于idom来说,我们每处理完一个点的sdom,就把它挂在sdom处。
然后处理所有挂在它父亲处的点v,求出点v到r路径上目前sdom最小的点设为u。
如果sdom(u) < sdom(v),说明此时v不满足第一种情况,但是u是对于v来说第二种情况的候选点,并且由于我们是倒序枚举u一定当前最小,我们直接把idom(v)设为u。
否则idom(v)=fa(x),这是由于第一种情况。
当我们做完之后会发现第二种情况我们的idom实际上求的是候选点而不是idom
所以再扫一遍把所有的idom求出来即可。

Code

例题:HDU 4694 Important Sisters

typedef vector<int> vec;
#define pb(a) push_back(a)
vec pre[N],dom[N];

int lst[N],nxt[N<<1],t[N<<1],l;
void add(int x,int y) {
    t[++l]=y;nxt[l]=lst[x];lst[x]=l;
}

int n,m,x,y,semi[N],idom[N];

int id[N],dfn[N],fa[N],tot;
void dfs(int x) {
    dfn[x]=++tot;id[tot]=x;
    rep(i,x) {
        pre[t[i]].pb(x);
        if (!dfn[t[i]]) {
            fa[t[i]]=x;
            dfs(t[i]);
        } 
    }
}

int father[N],val[N];
int get(int x) {
    if (father[x]==x) return x;
    int y=get(father[x]);
    if (dfn[semi[val[father[x]]]]return father[x]=y;
}

int smin(int x,int y) {return dfn[x]void solve() {
    fd(i,tot,2) {
        int x=id[i];
        if (!pre[x].empty())
            fo(j,0,pre[x].size()-1)
                if (dfn[pre[x][j]]else {
                    get(pre[x][j]);
                    semi[x]=smin(semi[x],semi[val[pre[x][j]]]);
                }
        father[x]=fa[x];dom[semi[x]].pb(x);
        if (!dom[fa[x]].empty())
            fo(j,0,dom[fa[x]].size()-1) {
                int v=dom[fa[x]][j];get(v);
                int u=val[v];
                idom[v]=(dfn[semi[u]]2,tot) {
        int x=id[i];
        if (idom[x]!=semi[x]) idom[x]=idom[idom[x]];
    }
}

参考资料:WC营员交流 王梦迪-支配树

你可能感兴趣的:(心情,总结,支配树,图论,支配树,dominator,tree,图论,学习笔记)