支配树(Dominator Tree)学习笔记

来源:互联网 发布:matlab centos ubuntu 编辑:程序博客网 时间:2024/05/12 15:45

因为是学习笔记所以是边学边写,以防自己到时候忘掉了=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]]]]<dfn[semi[val[x]]]) val[x]=val[father[x]];    return father[x]=y;}int smin(int x,int y) {return dfn[x]<dfn[y]?x:y;}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]]<dfn[x]) semi[x]=smin(semi[x],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]]<dfn[semi[v]])?u:fa[x];            }    }    fo(i,2,tot) {        int x=id[i];        if (idom[x]!=semi[x]) idom[x]=idom[idom[x]];    }}

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

原创粉丝点击