伪目录
- 给出支配树的定义
- 给出一些性质
- 介绍快速构造支配树的Lengauer-Tarjan算法及具体实现
支配树是啥
一个有源点的有向图,其支配树是满足下面条件的一个有向图:
对于支配树上一点,若断开此点,则源点必定不能到达它的任何儿子,并且能到达其他任意一个点。
不显然的,它是一棵树(当然后面会有证明)
支配树有很多实际用途,我都不知道。
一些性质
对于一个有向图,假设源点为,先从出发构造一棵dfs树。
定义:对于一个点,若一支配的点满足并且被支配的其他不含的支配点支配,则就是的最近支配点,记作。
通俗的讲,是离最近的那个支配点,并且能恰好支配。
Lemma 1: 支配关系不存在环。
Proof: 若支配,那么到必定经过,若支配则到达需要经过,此时并没有经过,产生矛盾。
Lemma 2: 除源点外,其他点有且仅有一个最近支配点。
Proof: 若支配,支配,则支配;若支配,支配,那么支配或支配,否则可以找到一条路径不经过而到达而矛盾。因此支配的所有点的集合构成了一个全序关系,因此总可以找到一个点满足上述最近支配点的定义。
Theorem 1: 若连接一个点和其最近支配点,那么这张图构成了一棵树,并且满足支配树的定义。
Proof: 由Lemma 1和Lemma 2可以得到这张图就是一棵树。容易证明,若断开,则源点不可能到达的任意一个儿子。对于其他点,由支配树的定义和Lemma 2可以推导出这个点不会受到是否断开的影响。
由Theorem 1,若得到了所有点的,则容易构造出这张图的支配树。
那么怎么求呢?
首先定义:定义一个点的半支配点为存在路径,使得除了,这条路径上任意一个点的dfs序都大于等于的dfs序,并且是所有满足条件的点中最小的那个,记作。
Lemma 3: 对于任意一点,为在dfs树上的祖先。
Proof: 若不是祖先,则可以找到一条只经过树边的路径,必定不经过。
Lemma 4: 对于任意一点,为在dfs树上的祖先。
Proof: 若不是祖先,若其dfs序小于的dfs序,则dfs时必定会经过而到达,此时就是的祖先,产生矛盾;否则,则可以找到一个点为在dfs树上的祖先,路径是一条满足定义的路径,并且的dfs序小于的dfs序,产生矛盾。
Lemma 5: 对于任意一点,为在dfs树上的祖先。
Proof: 若不是祖先,则可以找到一条的路径,其中经过dfs树边,显然不经过;这段路径上任意一个点的dfs序大于,由于Lemma 3,这段路径也不经过,因此找到了一条路径可以绕过,产生矛盾。
Lemma 6: 对于任意两点,若是的祖先,则是的祖先或是的祖先(上面两种情况都可以相等)。
Proof: 否则是的祖先,是的祖先,那么存在一条路径能绕过而到达进而到达,产生矛盾。
Theorem 2: 对于任意一点,若只经过树边的路径上,不包括的任意一点都满足是的祖先或二者相等,则。
Proof: 由Lemma 5,若支配,则。对的任意一条路径,取dfs序最大的点满足是的祖先;取dfs序最小的点,满足是的祖先或二者相等,容易发现这样的点必定存在。那么的就是,但是是的祖先,因此,那么任何的路径必定经过,因此支配。
Theorem 3: 对于任意一点,若只经过树边的路径上,不包括的点中,对于的dfs序最小的,满足是的祖先,那么。
Proof: 由Lemma 5和Lemma 6,是的祖先或二者相等。因此只要证明支配即可。类似Theorem 2的证明,取为的祖先,为的后代或二者相等,那么为,又由于是最大的,因此不可能是的后代;若是的祖先或相等,但是是的后代,那么就找到了一条绕过而到达的路径。因此就是。那么所有路径都经过,因此支配。
由Theorem 2和Theorem 3,我们可以轻松的由求出了。
Theorem 4: 对于任意一点,是满足下列条件两条件的dfs序最小的:1. 存在一条边且的dfs序小于的dfs序;2. ,的dfs序大于的dfs序,并且存在一个点满足存在一条边且是的祖先或相等。
Proof: 显然对于每一个,都存在一条满足要求的路径。由于后面的点dfs序一定大于等于,取的后面一个点,那么的dfs序必定等于,并且符合上述条件。
由Theorem 4可以很方便的求出。
具体实现
以dfs序的倒序枚举每个点,假设有一个数据结构,支持:将一个点作为另外一个点的父亲;查询这个点到根的最小值。
设当前枚举的点为,可以枚举的前驱,那么就是的前驱在数据结构上的最小值的最小值。
对于的父亲,每个并且是的后代或二者相等的在数据结构上的最小值就是Theorem 2和Theorem 3描述的最小值,直接更新的即可。
注意Theorem 3中的可能还没有被更新,那么暂时令,之后若就更新成。
数据结构可以采用带权并查集,时间复杂度
代码
//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;
}