支配树与Lengauer-Tarjan算法

伪目录

  1. 给出支配树的定义
  2. 给出一些性质
  3. 介绍快速构造支配树的Lengauer-Tarjan算法及具体实现

支配树是啥

一个有源点的有向图,其支配树是满足下面条件的一个有向图:

对于支配树上一点,若断开此点,则源点必定不能到达它的任何儿子,并且能到达其他任意一个点。

不显然的,它是一棵树(当然后面会有证明)

支配树有很多实际用途,我都不知道

一些性质

对于一个有向图,假设源点为 r r r,先从 r r r出发构造一棵dfs树。

定义:对于一个点 u u u,若一支配 u u u的点 w w w满足 w ̸ = u w\not= u w̸=u并且 w w w被支配 u u u的其他不含 u u u的支配点支配,则 w w w就是 u u u的最近支配点,记作 i d o m ( u ) idom(u) idom(u)

通俗的讲, w w w是离 u u u最近的那个支配点,并且能恰好支配 u u u

Lemma 1: 支配关系不存在环。

Proof: 若 a a a支配 b b b,那么 r r r b b b必定经过 a a a,若 b b b支配 a a a则到达 b b b需要经过 a a a,此时并没有经过 b b b,产生矛盾。

Lemma 2: 除源点外,其他点有且仅有一个最近支配点。

Proof: 若 a a a支配 b b b b b b支配 c c c,则 a a a支配 c c c;若 a a a支配 c c c b b b支配 c c c,那么 a a a支配 b b b b b b支配 a a a,否则可以找到一条路径不经过 a a a而到达 c c c而矛盾。因此支配 u u u的所有点的集合构成了一个全序关系,因此总可以找到一个点满足上述最近支配点的定义。

Theorem 1: 若连接一个点和其最近支配点,那么这张图构成了一棵树,并且满足支配树的定义。

Proof: 由Lemma 1和Lemma 2可以得到这张图就是一棵树。容易证明,若断开 u u u,则源点不可能到达 u u u的任意一个儿子。对于其他点,由支配树的定义和Lemma 2可以推导出这个点不会受到 u u u是否断开的影响。

由Theorem 1,若得到了所有点的 i d o m idom idom,则容易构造出这张图的支配树。

那么怎么求 i d o m idom idom呢?

首先定义:定义一个点 u u u的半支配点 w w w为存在路径 w → u w\rightarrow u wu,使得除了 w w w,这条路径上任意一个点的dfs序都大于等于 u u u的dfs序,并且 w w w是所有满足条件的点中最小的那个,记作 s d o m ( u ) sdom(u) sdom(u)

Lemma 3: 对于任意一点 u ̸ = r u\not=r u̸=r i d o m ( u ) idom(u) idom(u) u u u在dfs树上的祖先。

Proof: 若不是祖先,则可以找到一条只经过树边的路径,必定不经过 i d o m ( u ) idom(u) idom(u)

Lemma 4: 对于任意一点 u ̸ = r u\not=r u̸=r s d o m ( u ) sdom(u) sdom(u) u u u在dfs树上的祖先。

Proof: 若不是祖先,若其dfs序小于 u u u的dfs序,则dfs时必定会经过 s d o m ( u ) sdom(u) sdom(u)而到达 u u u,此时 s d o m ( u ) sdom(u) sdom(u)就是 u u u的祖先,产生矛盾;否则,则可以找到一个点 w w w s d o m ( u ) sdom(u) sdom(u)在dfs树上的祖先,路径 w → s d o m ( u ) → u w\rightarrow sdom(u)\rightarrow u wsdom(u)u是一条满足定义的路径,并且 w w w的dfs序小于 s d o m ( u ) sdom(u) sdom(u)的dfs序,产生矛盾。

Lemma 5: 对于任意一点 u ̸ = r u\not=r u̸=r i d o m ( u ) idom(u) idom(u) s d o m ( u ) sdom(u) sdom(u)在dfs树上的祖先。

Proof: 若不是祖先,则可以找到一条 r → s d o m ( u ) → u r\rightarrow sdom(u)\rightarrow u rsdom(u)u的路径,其中 r → s d o m ( u ) r\rightarrow sdom(u) rsdom(u)经过dfs树边,显然不经过 i d o m ( u ) idom(u) idom(u) s d o m ( u ) → u sdom(u)\rightarrow u sdom(u)u这段路径上任意一个点的dfs序大于 u u u,由于Lemma 3,这段路径也不经过 i d o m ( u ) idom(u) idom(u),因此找到了一条路径可以绕过 i d o m ( u ) idom(u) idom(u),产生矛盾。

Lemma 6: 对于任意两点 u ̸ = r , v ̸ = r u\not=r,v\not=r u̸=r,v̸=r,若 u u u v v v的祖先,则 u u u i d o m ( v ) idom(v) idom(v)的祖先或 i d o m ( v ) idom(v) idom(v) i d o m ( u ) idom(u) idom(u)的祖先(上面两种情况都可以相等)。

Proof: 否则 i d o m ( u ) idom(u) idom(u) i d o m ( v ) idom(v) idom(v)的祖先, i d o m ( v ) idom(v) idom(v) u u u的祖先,那么存在一条路径能绕过 i d o m ( v ) idom(v) idom(v)而到达 u u u进而到达 v v v,产生矛盾。

Theorem 2: 对于任意一点 u ̸ = r u\not=r u̸=r,若 s d o m ( u ) → u sdom(u)\rightarrow u sdom(u)u只经过树边的路径上,不包括 s d o m ( u ) sdom(u) sdom(u)的任意一点 v v v都满足 s d o m ( u ) sdom(u) sdom(u) s d o m ( v ) sdom(v) sdom(v)的祖先或二者相等,则 i d o m ( u ) = s d o m ( u ) idom(u)=sdom(u) idom(u)=sdom(u)

Proof: 由Lemma 5,若 s d o m ( u ) sdom(u) sdom(u)支配 u u u,则 i d o m ( u ) = s d o m ( u ) idom(u)=sdom(u) idom(u)=sdom(u)。对 r → u r\rightarrow u ru的任意一条路径,取dfs序最大的点 w w w满足 w w w s d o m ( u ) sdom(u) sdom(u)的祖先;取dfs序最小的点 x x x,满足 s d o m ( u ) sdom(u) sdom(u) x x x的祖先或二者相等,容易发现这样的点必定存在。那么 s d o m ( x ) sdom(x) sdom(x)的就是 w w w,但是 w w w s d o m ( u ) sdom(u) sdom(u)的祖先,因此 x = s d o m ( u ) x=sdom(u) x=sdom(u),那么任何 r → u r\rightarrow u ru的路径必定经过 s d o m ( u ) sdom(u) sdom(u),因此 s d o m ( u ) sdom(u) sdom(u)支配 u u u

Theorem 3: 对于任意一点 u ̸ = r u\not=r u̸=r,若 s d o m ( u ) → u sdom(u)\rightarrow u sdom(u)u只经过树边的路径上,不包括 s d o m ( u ) sdom(u) sdom(u)的点中,对于 s d o m ( v ) sdom(v) sdom(v)的dfs序最小的 v v v,满足 s d o m ( v ) sdom(v) sdom(v) s d o m ( u ) sdom(u) sdom(u)的祖先,那么 i d o m ( u ) = i d o m ( v ) idom(u)=idom(v) idom(u)=idom(v)

Proof: 由Lemma 5和Lemma 6, i d o m ( u ) idom(u) idom(u) i d o m ( v ) idom(v) idom(v)的祖先或二者相等。因此只要证明 i d o m ( v ) idom(v) idom(v)支配 u u u即可。类似Theorem 2的证明,取 w w w i d o m ( v ) idom(v) idom(v)的祖先, x x x i d o m ( v ) idom(v) idom(v)的后代或二者相等,那么 s d o m ( x ) sdom(x) sdom(x) w w w,又由于 s d o m ( v ) sdom(v) sdom(v)是最大的,因此 x x x不可能是 s d o m ( u ) sdom(u) sdom(u)的后代;若 x x x s d o m ( u ) sdom(u) sdom(u)的祖先或相等,但是是 i d o m ( v ) idom(v) idom(v)的后代,那么就找到了一条绕过 i d o m ( v ) idom(v) idom(v)而到达 v v v的路径。因此 x x x就是 i d o m ( v ) idom(v) idom(v)。那么所有路径都经过 i d o m ( v ) idom(v) idom(v),因此 i d o m ( v ) idom(v) idom(v)支配 u u u

由Theorem 2和Theorem 3,我们可以轻松的由 s d o m sdom sdom求出 i d o m idom idom了。

Theorem 4: 对于任意一点 u ̸ = r u\not= r u̸=r s d o m ( u ) sdom(u) sdom(u)是满足下列条件两条件的dfs序最小的 x x x:1. 存在一条边 x → u x\rightarrow u xu x x x的dfs序小于 u u u的dfs序;2. x = s d o m ( v ) x=sdom(v) x=sdom(v) v v v的dfs序大于 u u u的dfs序,并且存在一个点 w w w满足存在一条边 w → u w\rightarrow u wu v v v w w w的祖先或相等。

Proof: 显然对于每一个 x x x,都存在一条满足 s d o m sdom sdom要求的路径。由于 s d o m ( u ) sdom(u) sdom(u)后面的点dfs序一定大于等于 u u u,取 s d o m ( u ) sdom(u) sdom(u)的后面一个点 v v v,那么 s d o m ( v ) sdom(v) sdom(v)的dfs序必定等于 s d o m ( u ) sdom(u) sdom(u),并且 v v v符合上述条件。

由Theorem 4可以很方便的求出 s d o m sdom sdom

具体实现

以dfs序的倒序枚举每个点,假设有一个数据结构,支持:将一个点作为另外一个点的父亲;查询这个点到根的 s d o m sdom sdom最小值。

设当前枚举的点为 u u u,可以枚举 u u u的前驱 x x x,那么 s d o m ( u ) sdom(u) sdom(u)就是 u u u的前驱在数据结构上的 s d o m sdom sdom最小值的最小值。

对于 u u u的父亲 t t t,每个 s d o m ( w ) = t sdom(w)=t sdom(w)=t并且 w w w u u u的后代或二者相等的 w w w在数据结构上的 s d o m sdom sdom最小值就是Theorem 2和Theorem 3描述的最小值,直接更新 w w w i d o m idom idom即可。

注意Theorem 3中 v v v i d o m idom idom可能还没有被更新,那么暂时令 i d o m ( w ) = v idom(w)=v idom(w)=v,之后若 i d o m ( w ) ̸ = s d o m ( w ) idom(w)\not=sdom(w) idom(w)̸=sdom(w)就更新成 i d o m ( i d o m ( w ) ) idom(idom(w)) idom(idom(w))

数据结构可以采用带权并查集,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

代码

//dfn[i]为i的dfs序,idfn[i]是dfs序为i的点,het[i]是i的所有前驱,bkt[i]是所有sdom为i的点
//eval(i)为找到并查集中这个点到父亲的sdom中dfs序最小的那个
int getidom()
{
  for(int i=cnt; i>1; --i)
    {
      int u=idfn[i];
      for(int v:het[u])
        {
          if(!dfn[v])
            {
              continue;
            }
          int w=eval(v);
          if(dfn[sdom[w]]<dfn[sdom[u]])
            {
              sdom[u]=sdom[w];
            }
        }
      bkt[sdom[u]].push_back(u);
      int t=fa[u];
      dsu::fa[u]=t;
      for(int v:bkt[t])
        {
          int w=eval(v);
          idom[v]=(sdom[w]==sdom[v])?t:w;
        }
      bkt[t].clear();
    }
  for(int i=2; i<=cnt; ++i)
    {
      int u=idfn[i];
      idom[u]=(idom[u]==sdom[u])?(idom[u]):(idom[idom[u]]);
    }
  return 0;
}

你可能感兴趣的:(算法模板)