伪目录

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

支配树是啥

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

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

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

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

一些性质

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

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

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

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

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

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

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

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

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

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

那么怎么求idomidom呢?

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

Lemma 3: 对于任意一点u̸=ru\not=ridom(u)idom(u)uu在dfs树上的祖先。

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

Lemma 4: 对于任意一点u̸=ru\not=rsdom(u)sdom(u)uu在dfs树上的祖先。

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

Lemma 5: 对于任意一点u̸=ru\not=ridom(u)idom(u)sdom(u)sdom(u)在dfs树上的祖先。

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

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

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

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

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

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

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

由Theorem 2和Theorem 3,我们可以轻松的由sdomsdom求出idomidom了。

Theorem 4: 对于任意一点u̸=ru\not= rsdom(u)sdom(u)是满足下列条件两条件的dfs序最小的xx:1. 存在一条边xux\rightarrow uxx的dfs序小于uu的dfs序;2. x=sdom(v)x=sdom(v)vv的dfs序大于uu的dfs序,并且存在一个点ww满足存在一条边wuw\rightarrow uvvww的祖先或相等。

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

由Theorem 4可以很方便的求出sdomsdom

具体实现

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

设当前枚举的点为uu,可以枚举uu的前驱xx,那么sdom(u)sdom(u)就是uu的前驱在数据结构上的sdomsdom最小值的最小值。

对于uu的父亲tt,每个sdom(w)=tsdom(w)=t并且wwuu的后代或二者相等的ww在数据结构上的sdomsdom最小值就是Theorem 2和Theorem 3描述的最小值,直接更新wwidomidom即可。

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

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

代码

//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;
}