QOJ 9160 NOI2024 D2T3 树形图 题解

题面


A 性质

由于没有一类点,只要能到达所有点就是二类点。判一下即可。

BC 性质

\(1\) 为根建立 dfs 树,可以发现只有树边和返祖边。

定义:起点在 \(u\) 子树内而终点不在的边为 \(u\) 的出子树边。在这里出子树边总是返祖边。

考虑一个不是 \(1\) 的点 \(u\)

  1. \(u\) 想成为一类点,必须满足子树内有恰好一条出子树边。
  2. 记这条出子树边指向 \(x\) ,则 \(u\) 是一类点当且仅当 \(x\) 是一类点。

第二个条件成立,是因为:

  1. \(x\) 的出子树边不在 \(u\) 的子树内。
  2. 去掉 \(u\) 子树不影响 \(u,x\) 是不是一类点。

树上差分维护出子树边即可。

C 性质

显然只要对于每个一类点保留子树内出子树边就能满足所有一类点仍然是一类点(当然树边也要保留)。

注意到,\(u\) 是二类点,当且仅当能能保留一些边,使得 \(u\) 恰好有一条出子树边,且这条边指向了一个一类点或二类点。证明和求一类点类似。

对于一个非一类点 \(u\)

  1. 如果 \(u\) 子树内有 \(\ge 2\) 条必须保留的出子树边,显然不能成为二类点。
  2. 如果 \(u\) 子树内有 \(1\) 条必须保留的出子树边,显然这条边的终点总是一类点,则 \(u\) 总是二类点。

考虑维护所有点有无指向一/二类点的返祖边,每次如果是第一种情况就只传递父亲,否则传递父亲和所有指向它的返祖边。可以直接建图跑 bfs 来做。

一个可以帮助写代码的性质:

  • 一个点只有最多一条返祖边会被保留。

接下来只要找到任意一个一类点即可。

\(O(n\log n)\) 做法

要有一类点,必然会有一个点入度数为 \(1\)

取一个入度为 \(1\) 的点,记为 \(v\) ,入边为 \(u\rightarrow v\) ,假如有除了 \(v\) 的一类点,那么我们可以把 \(u,v\) 缩起来。具体的,把 \(v\) 的出边的起点改成 \(u\) ,然后把 \(v\) 删掉。

这样为什么是对的呢?考虑一条新图的不以 \(v\) 为起点或终点的路径,发现可以和原图的路径双射。

使用启发式合并维护出边,可以做到 \(O(n\log n)\)

\(O(n)\) 做法

随意找一个能到所有点的点作为根建立 dfs 树,不妨记为 \(1\)

一个点 \(u\) 想要成为一类点,必须到 \(1\) 有唯一路径。更进一步,发现 \(u\) 的出子树边唯一,那么 \(u\) 总是会走这条边,记走到 \(x\)\(x\) 的出子树边也必须唯一,以此类推可以得到 \(u\)\(1\) 的路径,可以发现这就是那条唯一路径。把路径上所有走出子树边走到的点称为关键点。

发现此时 \(u\) 能到所有点,那么所有非关键点必然是从一个关键点走树边到达的。把所有点按照最近关键祖先划分区域,并把区域按照关键点在 \(u\)\(1\) 的路径上的位置从前往后排序。

根据这个结构,可以注意到:

  1. 考虑前向边和横叉边,分类讨论:
    1. 前面 \(\rightarrow\) 后面,则肯定会产生额外出子树边,不存在这种情况。
    2. 后面 \(\rightarrow\) 前面,发现合法当且仅当终点在 \(u\)\(1\) 的路径上。
    3. 在同一个区域内,总是不合法。
  2. 考虑返祖边 \(x\rightarrow y\) ,发现如果跨区域则肯定是前 \(\rightarrow\) 后,此时肯定作为出子树边,无需考虑。否则,发现不会影响合法性。

得到充要条件:

  1. \(u\) 一直跳出子树边能到 \(1\) ,且每一步出子树边唯一。把这样跳出子树边到的点称为关键点。
  2. 所有前向边和横叉边指向 \(u\)\(1\) 路径上的点。
  3. 对于关键点 \(x\) ,记其出子树边起点为 \(y\) ,则不存在以 \(x\) 子树内为起点,以 \(x\)\(y\) 路径为终点的前向边和横叉边。

第三条其实就是处理同一个区域内的情况,但只要考虑第二条漏掉的情况。

需要求出一个点的出子树边,可以直接求 \(\operatorname{lca}\) 树上差分,是 \(O(n\log n)\) 的(也可以 \(O(n)\) ,但常数较大)。一个更好的做法是直接维护 dfn 序最小和次小的子树内出边,可以做到 \(O(n)\)

发现出子树边的 dfn 肯定小于当前点,那么按照 dfn 递推即可处理前两条。第二条可以树上前缀和维护。

对于第三条,对每个点 \(x\) 求出,指向 \(x\) 祖先的前向边和横叉边中,起点和 \(x\)\(\operatorname{lca}\) 深度最大的那个。

考虑使用逆 dfn 维护,即后序遍历得到的 dfn 。这样的好处是:

  1. 一个子树的根是最大值,且子树连续。
  2. 对于前向边和横叉边,满足起点大于终点。返祖边满足起点小于终点。

只需要对每个点求出指向它的前向边和横叉边中起点逆 dfn 的最小值,然后做前缀 \(\min\) 即可。

复杂度 \(O(n)\)代码

posted @ 2024-12-23 20:55  Z_301  阅读(182)  评论(0)    收藏  举报