【学习笔记】支配树

【前言】

  • 本文为博主的转载,由于博主看到的文章同样是转载的,无法注明原文出处。
  • 博主在原文的基础上修改了格式、措辞和一些小错误,并适当添加了一些自己的理解。

【支配树简介】

  • 对于一个单源有向图上的每个点 w w w,都存在点 d d d满足去掉 d d d之后起点无法到达 w w w,我们称作 d d d支配 w w w d d d w w w的一个支配点。

  • 支配 w w w的点可以有多个,但是至少会有一个。显然,对于起点以外的点,它们都有两个平凡的支配点,一个是自己,一个是起点。

  • 在支配 w w w的点中,如果一个支配点 i ≠ w i \neq w i̸=w满足 i i i w w w剩下的所有支配点支配,则这个 i i i称作 w w w的最近支配点 ( i m m e d i a t e   d o m i n a t o r ) (immediate\ dominator) (immediate dominator) ,记作 i d o m ( w ) idom(w) idom(w)

  • 定理 1 1 1 :我们把图的起点称作 r r r,除 r r r以外每个点均存在唯一的 i d o m idom idom

    证明: 如果 a a a支配 b b b b b b支配 c c c,则 a a a一定支配 c c c,因为到达 c c c的路径都经过了 b b b所以必须经过 a a a。如果 b b b支配 c c c a a a支配 c c c,则 a a a支配 b b b(或者 b b b支配 a a a),否则存在从 r r r b b b再到 c c c的路径绕过 a a a,与 a a a支配 c c c矛盾。这就意味着支配定义了点 w w w的支配点集合上的一个全序关系,所以一定可以找到一个“最小”的元素使得所有元素都支配它。

  • 于是, i d o m ( w ) → w   ( w ≠ r ) idom(w) \to w\ (w\ne r) idom(w)w (w̸=r) 的边,就能得到一棵树,其中每个点支配它子树中的所有点,它就是支配树。

  • 下文介绍了构建支配树的 L e n g a u e r − T a r j a n Lengauer-Tarjan LengauerTarjan 算法,其时间复杂度为 O ( N L o g N + M ) O(NLogN+M) O(NLogN+M) ,空间复杂度为 O ( N + M ) O(N+M) O(N+M)

  • 为了能够求出支配树,我们下面来推导一下需要用到的一些定理。

【定理推导】

  • 首先,我们会使用一棵 D F S DFS DFS 树来帮助我们计算。从起点出发进行 D F S DFS DFS 就可以得到一棵 D F S DFS DFS 树。

  • 原图中的边被分为了以下两类:在 D F S DFS DFS 树上出现的边称作树边,剩下的边称为非树边。非树边也可以分为几类:从祖先指向后代(前向边),从后代指向祖先(后向边),从一棵子树內指向另一棵子树内(横叉边)。树边是我们非常熟悉的,所以着重考虑一下非树边。

  • 我们按照 D F S DFS DFS 到的先后顺序给点从小到大编号(在下面的内容中我们通过这个比较两个节点),那么前向边总是由编号小的指向编号大的,后向边总是由大指向小,横叉边也总是由大指向小。现在在 D F S DFS DFS 树上我们要证明一些重要的引理:

  • 引理 1 1 1 (路径引理):如果两个点 v , w v,w v,w满足 v ≤ w v \leq w vw,那么任意 v v v w w w的路径经过 v , w v,w v,w的公共祖先。(注意这里不是说 L C A LCA LCA

    证明: 如果 v , w v,w v,w其中一个是另一个的祖先显然成立。否则删掉起点到 L C A LCA LCA 路径上的所有点(这些点是 v , w v,w v,w的公共祖先),那么 v v v w w w在两棵子树内,并且因为公共祖先被删去,无法通过后向边到达子树外面,前向边也无法跨越子树,而横叉边只能从大到小,所以从 v v v出发不能离开这颗子树到达 w w w。所以如果本来 v v v能够到达 w w w,就说明这些路径必须经过 v , w v,w v,w的公共祖先。

  • 在继续之前,我们先约定一些记号:

V V V代表图的点集, E E E代表图的边集。 a → b a \to b ab代表从点 a a a直接经过一条边到达点 b b b a ⇝ b a \leadsto b ab代表从点 a a a经过某条路径到达点 b b b a → ˙ b a \dot \to b a˙b代表从点 a a a经过 D F S DFS DFS 树上的树边到达点 b b b a a a b b b D F S DFS DFS 树上的祖先), a → + b a \overset{+}{\to} b a+b代表 a → ˙ b a \dot \to b a˙b a ≠ b a \neq b a̸=b

  • 定义:半支配点 ( s e m i − d o m i n a t o r ) (semi-dominator) (semidominator) :对于 w ≠ r w \neq r w̸=r,它的半支配点定义为 s d o m ( w ) = min ⁡ { v   ∣   ∃ ( v 0 , v 1 , ⋯   , v k − 1 , v k ) , v 0 = v , v k = w , ∀ 1 ≤ i ≤ k − 1 , v i > w } sdom(w)=\min\{ v\ |\ \exists (v_0,v_ 1 ,\cdots,v_{k- 1 },v_k), v_0 = v, v_k = w, \forall 1 \leq i \leq k- 1 , v_i>w \} sdom(w)=min{v  (v0,v1,,vk1,vk),v0=v,vk=w,1ik1,vi>w}

    这个定义可以理解为从 v v v出发,可以绕过小于 w w w的所有点到达 w w w(只能以大于 w w w 的点作为落脚点)的最小的 v v v

    注意这只是个辅助定义,并不是真正的支配点。

  • 引理 2 2 2 :对于任意 w ≠ r w \neq r w̸=r,有 i d o m ( w ) → + w idom(w) \overset{+}{\to} w idom(w)+w

    证明: 如果不是这样的话就可以直接通过树边不经过 i d o m ( w ) idom(w) idom(w)就到达 w w w了,与 i d o m idom idom定义矛盾。

  • 引理 3 3 3 :对于任意 w ≠ r w \neq r w̸=r,有 s d o m ( w ) → + w sdom(w) \overset{+}{\to} w sdom(w)+w

    证明: 对于 w w w D F S DFS DFS 树上的父亲 f a w fa_w faw f a w → w fa_w \to w faww这条路径只有两个点,所以满足 s d o m sdom sdom定义中的条件,于是它是 s d o m ( w ) sdom(w) sdom(w)的一个候选。所以 s d o m ( w ) ≤ f a w sdom(w) \leq fa_w sdom(w)faw。在这里我们就可以使用路径引理证明 s d o m ( w ) sdom(w) sdom(w)不可能在另一棵子树,因为如果是那样的话就会经过 s d o m ( w ) sdom(w) sdom(w) w w w的一个公共祖先,公共祖先的编号一定小于 w w w,所以不可行。于是 s d o m ( w ) sdom(w) sdom(w)就是 w w w的真祖先。

  • 引理 4 4 4 :对于任意 w ≠ r w \neq r w̸=r,有 i d o m ( w ) → ˙ s d o m ( w ) idom(w) \dot \to sdom(w) idom(w)˙sdom(w)

    证明: 如果不是这样的话,按照 s d o m sdom sdom的定义,就会有一条路径是 r → ˙ s d o m ( w ) ⇝ w r \dot \to sdom(w) \leadsto w r˙sdom(w)w不经过 i d o m ( w ) idom(w) idom(w)了,与 i d o m idom idom定义矛盾。

  • 引理 5 5 5 :对于满足 v → ˙ w v \dot \to w v˙w的点 v , w v,w v,w v → ˙ i d o m ( w ) v \dot \to idom(w) v˙idom(w) i d o m ( w ) → ˙ i d o m ( v ) idom(w) \dot \to idom(v) idom(w)˙idom(v)

    直观地理解就是 i d o m ( w ) idom(w) idom(w) w w w的路径两两之间边不相交或者存在完全包含关系。

    证明: 如果不是这样的话,就说明 i d o m ( v ) → + i d o m ( w ) → + v → + w idom(v) \overset{+}{\to} idom(w) \overset{+}{\to} v \overset{+}{\to} w idom(v)+idom(w)+v+w,那么存在路径 r → ˙ i d o m ( v ) ⇝ v → + r \dot \to idom(v) \leadsto v \overset{+}{\to} r˙idom(v)v+不经过 i d o m ( w ) idom(w) idom(w)到达了 w w w(因为 i d o m ( w ) idom(w) idom(w) i d o m ( v ) idom(v) idom(v)的真后代,一定不支配 v v v,所以存在绕过 i d o m ( w ) idom(w) idom(w)到达 v v v的路径),矛盾。

  • 上面这 5 5 5 条引理都比较简单,但是非常重要的性质。接下来我们要证明几个定理,它们揭示了 i d o m idom idom s d o m sdom sdom的关系。证明会比上面的复杂一点。

  • 定理 2 2 2 :对于任意 w ≠ r w \neq r w̸=r,如果所有满足 s d o m ( w ) → + u → ˙ w sdom(w) \overset{+}{\to} u \dot \to w sdom(w)+u˙w u u u也满足 s d o m ( u ) ≥ s d o m ( w ) sdom(u) \geq sdom(w) sdom(u)sdom(w),那么 i d o m ( w ) = s d o m ( w ) idom(w) = sdom(w) idom(w)=sdom(w)

    证明: 条件可以写为 s d o m ( w ) → ˙ s d o m ( u ) → + u → ˙ w sdom(w) \dot \to sdom(u) \overset{+}{\to} u \dot \to w sdom(w)˙sdom(u)+u˙w

    由上面的引理 4 4 4 知道 i d o m ( w ) → ˙ s d o m ( w ) idom(w) \dot \to sdom(w) idom(w)˙sdom(w),所以只要证明 s d o m ( w ) sdom(w) sdom(w)支配 w w w就可以保证是最近支配点了。对任意 r r r w w w的路径,取上面最后一个编号小于 s d o m ( w ) sdom(w) sdom(w) x x x(如果 s d o m sdom sdom就是 r r r的话显然定理成立),它必然有个后继 y y y满足 s d o m ( w ) → ˙ y → ˙ w sdom(w) \dot \to y \dot \to w sdom(w)˙y˙w(否则 x x x会变成 s d o m ( w ) sdom(w) sdom(w)),我们取最小的那个 y y y。同时,如果 y y y不是 s d o m ( w ) sdom(w) sdom(w),根据条件, s d o m ( y ) ≥ s d o m ( w ) sdom(y) \geq sdom(w) sdom(y)sdom(w),所以 x x x不可能是 s d o m ( y ) sdom(y) sdom(y),这就意味着 x x x y y y的路径上一定有一个 v v v满足 x → + v → + y x \overset{+}{\to} v \overset{+}{\to} y x+v+y,因为 x x x是小于 s d o m ( w ) sdom(w) sdom(w)的最后一个,所以 v v v也满足 s d o m ( w ) → ˙ v → ˙ w sdom(w) \dot \to v \dot \to w sdom(w)˙v˙w,但是我们取的 y y y已经是最小的一个了,矛盾。于是 y y y只能是 s d o m ( w ) sdom(w) sdom(w),那么我们就证明了对于任意路径都要经过 s d o m ( w ) sdom(w) sdom(w),所以 s d o m ( w ) sdom(w) sdom(w)就是 i d o m ( w ) idom(w) idom(w)

  • 定理 3 3 3 :对于任意 w ≠ r w \neq r w̸=r,令 u u u为所有满足 s d o m ( w ) → + u → ˙ w sdom(w) \overset{+}{\to} u \dot \to w sdom(w)+u˙w u u u s d o m ( u ) sdom(u) sdom(u)最小的一个,那么 s d o m ( u ) ≤ s d o m ( w ) ⇒ i d o m ( w ) = i d o m ( u ) sdom(u) \leq sdom(w) \Rightarrow idom(w) = idom(u) sdom(u)sdom(w)idom(w)=idom(u)

    证明: 条件可以写为 s d o m ( u ) → ˙ s d o m ( w ) → + u → ˙ w sdom(u) \dot \to sdom(w) \overset{+}{\to} u \dot \to w sdom(u)˙sdom(w)+u˙w

    由引理 5 5 5 ,有 i d o m ( w ) → ˙ i d o m ( u ) idom(w) \dot \to idom(u) idom(w)˙idom(u) u → ˙ i d o m ( w ) u \dot \to idom(w) u˙idom(w),由引理 4 4 4 排除后面这种。所以只要证明 i d o m ( u ) idom(u) idom(u)支配 w w w即可。类似定理 2 2 2 的证明,我们取任意 r r r w w w路径上最后一个小于 i d o m ( u ) idom(u) idom(u) x x x(如果 i d o m ( u ) idom(u) idom(u) r r r的话显然定理成立),路径上必然有个后继 y y y满足 i d o m ( u ) → ˙ y → ˙ w idom(u) \dot \to y \dot \to w idom(u)˙y˙w(否则 x x x会变成 s d o m ( w ) sdom(w) sdom(w)),我们取最小的一个 y y y。类似上面的证明,我们知道 x x x y y y的路径上不能有点 v v v满足 i d o m ( u ) → ˙ v → + y idom(u) \dot \to v \overset{+}{\to} y idom(u)˙v+y,于是 x x x成为 s d o m ( y ) sdom(y) sdom(y)的候选,所以 s d o m ( y ) ≤ x sdom(y) \leq x sdom(y)x。那么根据条件我们也知道了 y y y不能是 s d o m ( w ) sdom(w) sdom(w)的真后代,于是 y y y满足 i d o m ( u ) → ˙ y → ˙ s d o m ( w ) idom(u) \dot \to y \dot \to sdom(w) idom(u)˙y˙sdom(w)。但是我们注意到因为 s d o m ( y ) ≤ x sdom(y) \leq x sdom(y)x,存在一条路径 r → ˙ s d o m ( y ) ⇝ y → ˙ u r \dot \to sdom(y) \leadsto y \dot \to u r˙sdom(y)y˙u,如果 y y y不是 i d o m ( u ) idom(u) idom(u)的话这就是一条绕过 i d o m ( u ) idom(u) idom(u)的到 u u u的路径,矛盾,所以 y y y必定是 i d o m ( u ) idom(u) idom(u)。所以任意到 w w w的路径都经过 i d o m ( u ) idom(u) idom(u),所以 i d o m ( w ) = i d o m ( u ) idom(w)=idom(u) idom(w)=idom(u)

  • 完成了上面两个定理的证明,我们就能够通过 s d o m sdom sdom求出 i d o m idom idom了。

  • 推论 1 1 1 :对于 w ≠ r w \neq r w̸=r,令 u u u为所有满足 s d o m ( w ) → + u → ˙ w sdom(w) \overset{+}{\to} u \dot \to w sdom(w)+u˙w u u u s d o m ( u ) sdom(u) sdom(u)最小的一个,有

    i d o m ( w ) = { s d o m ( w ) ( s d o m ( u ) = s d o m ( w ) ) i d o m ( u ) ( s d o m ( u ) < s d o m ( w ) ) idom(w) = \left \{ \begin{aligned}& sdom(w)&(sdom(u)=sdom(w))&\\ &idom(u)&(sdom(u)<sdom(w))&\end{aligned} \right . idom(w)={sdom(w)idom(u)(sdom(u)=sdom(w))(sdom(u)<sdom(w))

    推论 1 1 1 可以通过定理 2 2 2 和定理 3 3 3 可以直接得到。这里一定有 s d o m ( u ) ≤ s d o m ( w ) sdom(u) \leq sdom(w) sdom(u)sdom(w),因为 w w w也是 u u u的候选。

  • 接下来我们的问题是,直接通过定义计算 s d o m sdom sdom很低效,我们需要更加高效的方法,所以我们证明下面这个定理:

  • 定理 4 4 4 :对于任意 w ≠ r w \neq r w̸=r

    s d o m ( w ) = m i n ( v   ∣   ( v , w ) ∈ E , v < w ∪ s d o m ( u )   ∣   u > w , ∃ ( v , w ) ∈ E , u → ˙ v ) sdom(w) = min({v\ |\ (v, w) \in E , v < w} \cup {sdom(u)\ |\ u > w , \exists (v, w) \in E , u \dot \to v} ) sdom(w)=min(v  (v,w)E,v<wsdom(u)  u>w,(v,w)E,u˙v)

    证明: 令右侧为 x x x,显然右侧的点集中都存在路径绕过 w w w之前的点,所以 s d o m ( w ) ≤ x sdom(w) \leq x sdom(w)x。然后我们考虑 s d o m ( w ) sdom(w) sdom(w) w w w的绕过 w w w之前的点的路径,如果只有一条边,那么必定满足 ( s d o m ( w ) , w ) ∈ E (sdom(w),w) \in E (sdom(w),w)E s d o m ( w ) < w sdom(w)<w sdom(w)<w,所以此时 x ≤ s d o m ( w ) x \leq sdom(w) xsdom(w);如果多于一条边,令路径上 w w w的上一个点为 l a s t last last,我们取路径上除两端外满足 p → ˙ l a s t p \dot \to last p˙last的最小的 p p p(一定能取得这样的 p p p,因为 l a s t last last p p p的候选)。因为这个 p p p是最小的,所以 s d o m ( w ) sdom(w) sdom(w) p p p的路径必定绕过了 p p p之前的所有点,于是 s d o m ( w ) sdom(w) sdom(w) s d o m ( p ) sdom(p) sdom(p)的候选,所以 s d o m ( p ) ≤ s d o m ( w ) sdom(p) \leq sdom(w) sdom(p)sdom(w)。同时, s d o m ( p ) sdom(p) sdom(p)还满足右侧的条件( p p p在绕过 w w w之前的点的路径上,于是 p > w p>w p>w,并且 p → ˙ l a s t p\dot \to last p˙last,同时 l a s t last last直接连到了 w w w),所以 s d o m ( p ) sdom(p) sdom(p) x x x的候选, x ≤ s d o m ( p ) x \leq sdom(p) xsdom(p)。所以 x ≤ s d o m ( p ) ≤ s d o m ( w ) x \leq sdom(p) \leq sdom(w) xsdom(p)sdom(w) x ≤ s d o m ( w ) x \leq sdom(w) xsdom(w)。综上, s d o m ( w ) ≤ x sdom(w) \leq x sdom(w)x x ≤ s d o m ( w ) x \leq sdom(w) xsdom(w),所以 x = s d o m ( w ) x=sdom(w) x=sdom(w)

  • 现在最困难的步骤已经完成了,我们得到了 s d o m sdom sdom的一个替代定义,而且这个定义里面的形式要简单得多。这种基本的树上操作我们是非常熟悉的,所以没有什么好担心的了。接下来就可以给出我们需要的算法了。

【构造流程】

  • 以下是算法的简要流程

    1 1 1 、初始化、跑一遍 D F S DFS DFS 得到 D F S DFS DFS 树和标号
    2 2 2 、按标号从大到小求出 s d o m sdom sdom(利用定理 4 4 4
    3 3 3 、通过推论 1 1 1 求出所有能确定的 i d o m idom idom,剩下的点记录下和哪个点的 i d o m idom idom是相同的
    4 4 4 、按照标号从小到大再跑一次,得到所有点的 i d o m idom idom

  • 以下是算法的具体实现细节

  • 大致要维护的东西:
    p ( x ) p(x) p(x) 标号为 x x x的点 u u u
    b ( u ) b(u) b(u) 有边直接连到 u u u的点集
    c ( u ) c(u) c(u) s d o m sdom sdom为点 u u u的点集
    f a t h e r ( u ) father(u) father(u) u u u D F S DFS DFS 树上的父亲 f a u fa_u fau
    以及 i d o m idom idom s d o m sdom sdom数组

  • 这里多说一句,由于我们上文的推导中我们经常会用一个点的 D F S DFS DFS 序来代替一个点来论述,我们有时会无法分清一个数组的下标和数值究竟代表一个点 x x x 在原图中的标号还是它的 D F S DFS DFS 序。

  • 为了统一,我们不妨规定数组的下标一律使用点在原图中的标号,除了计算过程中的 i d o m idom idom s d o m sdom sdom数组记录点的 D F S DFS DFS 序标号以外,其余数组一律记录点在原图中的标号。

  • 算法的第 1 1 1 步没什么特别的,规规矩矩地 D F S DFS DFS 一次即可,同时初始化 s d o m sdom sdom为自己(这是为了实现方便)。

  • 2 2 2 、第 3 3 3 步可以一起做。通过一个辅助数据结构维护一个森林,支持加入一条边 ( l i n k ( u , v ) ) (link(u,v) ) (link(u,v)) 和查询点到根路径上的点的 s d o m sdom sdom的最小值对应的点 ( h o m e ( u ) ) ( home(u) ) (home(u)) 。那么我们求每个点的 s d o m sdom sdom只需要对它的所有直接前驱 h o m e home home一次,求得前驱中的 s d o m sdom sdom最小值即可。因为定理 4 4 4 中的第一类点编号比它小,它们还没有处理过,所以自己就是根, h o m e home home就能取得它们的值;对于第二类点, h o m e home home查询的就是满足 u → ˙ v u \dot \to v u˙v u u u s d o m ( u ) sdom(u) sdom(u)的最小值。所以这么做和定理 4 4 4 是一致的。

  • 然后把该点加入它的 s d o m sdom sdom c c c里,连上它与父亲的边。现在它父亲到它的这棵子树中已经处理完了,所以可以对父亲的 c c c里的每个点求一次 i d o m idom idom并且清空 c c c。对于 c c c里的每个点 v v v,求出 h o m e ( v ) home(v) home(v),此时 f a t h e r ( w ) → + h o m e ( v ) → ˙ v father(w) \overset{+}{\to} home(v) \dot \to v father(w)+home(v)˙v,于是直接按照推论 1 1 1 ,如果 s d o m ( h o m e ( v ) ) = s d o m ( v ) sdom(home(v))=sdom(v) sdom(home(v))=sdom(v),则 i d o m ( v ) = s d o m ( v ) = f a t h e r ( w ) idom(v)=sdom(v)=father(w) idom(v)=sdom(v)=father(w);否则可以记下 i d o m ( v ) = i d o m ( h o m e ( v ) ) idom(v)=idom(home(v)) idom(v)=idom(home(v)),实现时我们可以写成 i d o m ( v ) = h o m e ( v ) idom(v)=home(v) idom(v)=home(v),留到第 4 4 4 步处理。

  • 最后从小到大扫一遍完成第 4 4 4 步,对于每个 u u u,如果 i d o m ( u ) = s d o m ( u ) idom(u)=sdom(u) idom(u)=sdom(u)的话,就已经是第 3 3 3 步求出的正确的 i d o m idom idom了,否则就证明这是第 3 3 3 步留下的待处理点,令 i d o m ( u ) = i d o m ( i d o m ( u ) ) idom(u)=idom(idom(u)) idom(u)=idom(idom(u))即可。

  • 至于这个辅助数据结构,我们可以选择并查集。不过因为我们需要查询到根路径上的信息,所以不方便按秩合并,但是我们仍然可以路径压缩,压缩时保留路径上的最值就可以了。这样做的话,最终的时间复杂度是 O ( N L o g N + M ) O(NLogN+M) O(NLogN+M)

【代码】

  • 模板题【HDU4694】
#include
using namespace std;
const int MAXN = 50005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, m, timer, root, dfn[MAXN], p[MAXN], father[MAXN];
int idom[MAXN], sdom[MAXN], f[MAXN], home[MAXN];
long long ans[MAXN]; vector <int> a[MAXN], b[MAXN], c[MAXN];
void dfs(int pos) {
	dfn[pos] = ++timer, p[timer] = pos;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (dfn[a[pos][i]] == 0) {
			father[a[pos][i]] = pos;
			dfs(a[pos][i]);
		}
}
int F(int x) {
	if (f[x] == x) return x;
	int tmp = f[x];
	f[x] = F(f[x]);
	if (sdom[home[tmp]] < sdom[home[x]]) home[x] = home[tmp];
	return f[x];
}
int gethome(int x) {
	F(x);
	return home[x];
}
void work(int pos, long long sum) {
	ans[pos] = sum;
	for (unsigned i = 0; i < a[pos].size(); i++)
		work(a[pos][i], sum + a[pos][i]);
}
int main() {
	while (scanf("%d%d", &n, &m) != EOF) {
		for (int i = 1; i <= n; i++) {
			a[i].clear();
			b[i].clear();
			c[i].clear();
		}
		for (int i = 1; i <= m; i++) {
			int x, y; read(x), read(y);
			a[x].push_back(y);
			b[y].push_back(x);
		}
		memset(dfn, 0, sizeof(dfn));
		timer = 0; dfs(root = n);
		for (int i = 1; i <= timer; i++) {
			sdom[p[i]] = i;
			idom[p[i]] = 0;
			f[p[i]] = home[p[i]] = p[i];
		}
		for (int i = timer; i >= 2; i--) {
			int tmp = p[i];
			for (unsigned j = 0; j < b[tmp].size(); j++)
				if (dfn[b[tmp][j]]) chkmin(sdom[tmp], sdom[gethome(b[tmp][j])]);
			c[sdom[tmp]].push_back(tmp);
			f[tmp] = father[tmp];
			tmp = dfn[father[tmp]];
			for (unsigned j = 0; j < c[tmp].size(); j++) {
				int tnp = gethome(c[tmp][j]);
				if (sdom[tnp] == tmp) idom[c[tmp][j]] = tmp;
				else idom[c[tmp][j]] = dfn[tnp];
			}
			c[tmp].clear();
		}
		for (int i = 1; i <= n; i++)
			a[i].clear();
		for (int i = 2; i <= timer; i++) {
			int tmp = p[i];
			if (sdom[tmp] == idom[tmp]) idom[tmp] = p[idom[tmp]];
			else idom[tmp] = idom[p[idom[tmp]]];
			sdom[tmp] = p[sdom[tmp]];
			a[idom[tmp]].push_back(tmp);
		}
		memset(ans, 0, sizeof(ans));
		work(root, root);
		for (int i = 1; i <= n; i++) {
			write(ans[i]);
			if (i == n) putchar('\n');
			else putchar(' ');
		}
	}
	return 0;
}

你可能感兴趣的:(【类型】学习笔记,【数据结构】支配树)