虚树
前情提要:本文中括号较多,有的括号表示补充说明,有的括号将定语分层方便阅读理解。
首先列一个树上问题解决表(并不是很全)。
多想想dfs栈和dfs作差。
| 询问 | 解决思路 |
|---|---|
| 整棵树的问题 | 自底向上树上贪心/树形DP |
| 链上问题 | 树链剖分 |
| 子树问题 | 线段树合并/dsu on tree/dfs序转成区间问题 |
| 给定点集询问 | 建虚树 |
| 涉及加边删边 | LCT |
静态虚树
虚数的大小为 (m为关键点数量),具体来说,。
虚树上的点都是原树上的点,但虚树上的边可能是原树的多条边缩成的。
具体来说,虚树首先包含了所有关键点,其次,虚树还包含了所有(有两个及以上(子树中含关键点)的儿子)的非关键点。
可以理解成关键点往上跑的时候在这些点交汇。
虚树中并不包含(仅有一个(子树中含关键点)的儿子)的非关键点,因为虚树上这个点的存在是不必要的,我们可以把它与父亲的连边和它与关键儿子(即子树中含关键点的儿子)的连边接在一起变成一条边,从而舍弃这个节点,来保证我们虚树的大小为 。
举个例子,譬如说下面这棵树的2和5是关键点:

那么这棵树的虚树是:

下面我们给出基于倍增求 的 的虚树的建立方法:
(虚树点集序列记为 ,边集序列记为 )
- 将所有关键点按dfs序排序并加入 。
- 将 加入 。
- 对 排序去重。
- 将 加入 。
注意:由于该算法复杂度小于 ,故清空虚树数组时务必使用 的处理方式。
下面给出例题CF631D的AC Code:
1 | // Problem: CF613D Kingdom and its Cities |
动态虚树(trick:虚树大小为任意dfs序上相邻关键点(第一个和最后一个也算)距离和/2)
在刚刚的静态虚树中,我们把虚树建了出来,但如果动态往点集中加点的话,虚树的复杂度和输入复杂度就不对等了,所以借鉴刚刚的思想,我们可以用 维护一个虚树点集而不维护边集,从而实现动态加点同时维护一些值,同时码量短了许多。
据说可以分好多种情况,不过OI中神奇的事情就是很多时候可以用一份代码囊括多种情况~
(p.s. stO 小粉兔 Orz 我尝试对题解区兔队的题解进行修改,结果发现这份代码已经是简洁精妙的极致)
下面给出例题P3320 [SDOI2015]寻宝游戏的AC Code:
1 | // Problem: P3320 [SDOI2015]寻宝游戏 |
取其精华:
1 |
|
此代码就是给定边权动态求虚树大小的模板(注意最后的ans/2)。
动态虚树不可完全替代虚树,许多需要将树建出来再处理的问题仍需要用静态虚树。
有的时候我们也需要简单小巧的 建虚数,如下:
1 | F(i,1,m) vis[p[i]]=1; |
注意多出来的点也是需要向上跳的,所以 i<=m 的条件是一个动态条件。