虚树

考虑一颗完整的树 \(T\),其中假设有一些关键点,那么我们考虑标记上所有标记点之间的点。主要用于我们希望寻找一些包含关键点的信息,比如动规一个子树内一个关键点没有,那么遍历是没有意义的。但是现在这样标记,对于我们的减少复杂度是没有意义的。我们需要去掉里面的“非分支”节点(度数为 \(2\))。去掉后我们在相邻的点之间重新连边,以得到一颗虚树 \(T_v\)

我们希望做树形动规,原来对整棵树动规,现在需要对树上部分节点(关键点)进行动规,或者说其他节点的信息没有那么重要。在做动规的时候,相当于是一个不断合并子树信息的过程。我们可以认为其他不存在关键点子树的信息如果可以快速处理出来的话切对于两个关键点之间的链也可以求出即可。

问题变成给出树及 \(k\) 个关键点 \(s_1\cdots s_k\),求:\(\{s_1,\cdots s_k\}\cup \{l(x,y)|x,y\in s\}\)。考虑 dfs 序:对于集合 \(S\)\(l(S)=l(x,y)\),其中 \(x,y\)\(S\) 中 dfs 序最小和最大的点。那么关键点两两 lca 怎么实现?我们不妨假设 \(d(s_1)<d(s_2)<\cdots <d(s_k)\)。那么关键点之间 lca 集合相当于 \(l(s_{i-1},s_i)\) 的集合。考虑对于 \(d(s_i)<d(s_j)\),如果对于一个新的 \(d(s_k)>d(s_j)\),那么如果 \(l(s_k,s_{v}))\) 没有出现,则它们一定在一个别的子树中发现是错的。

那么得到虚树 \((k,s_1\cdots s_k)\) 的完整算法流程就是:

  1. 按 dfs 序排序 \(s\)
  2. \(s\leftarrow s\cup \{l(s_i,s_{i+1})\}\) 得到虚树中所有点。
  3. 排序去重。

现在需要拼回一棵树(即连边)。我们只需要连最近的祖先儿子关系即可。

  1. 对于 \(i:2\rightarrow k^\prime\),连边 \((l(s_{i-1},s_i),s_i)\)
posted @ 2025-12-11 15:27  tanghg  阅读(1)  评论(0)    收藏  举报