虚树

虚树

在一些问题中我们只关心关键点的信息同时我们需要维护他们之间的树形结构于是可以想到将它们和它们之间的 $\rm LCA$ 拉出来建一颗树这棵树就被称作虚树

有一个朴素的想法就是对它们两两求 $\rm LCA$并且对求出的 $\rm LCA$ 再求 $\rm LCA$中途判重最后按照祖先关系连边

经过观察可以发现我们只需要按照 $\rm dfs$ 虚从小到大的顺序枚举每个点将相邻两点求 $\rm LCA$ 即可具体来说我们可以维护一个栈这个栈中存的是当前点在虚树上的所有祖先设当前栈顶为 $v$对于新加入的结点 $u$我们对它进行分类讨论

  • $\rm LCA(u,v)=v$那么直接将 $u$ 入栈
  • $\rm LCA(u,v)$ 是栈中某个不为 $v$ 的点则弹栈直到 $\rm LCA(u,v)$每弹一个就向栈中比它深度多 $1$ 的结点连边最后并把 $u$ 入栈
  • $\rm LCA(u,v)$ 不在栈中那么弹栈直到栈顶的 $\rm dfs$ 序小于 $\rm LCA(u,v)$$\rm dfs$每弹一个就向栈中比它深度多 $1$ 的结点连边并把最后一个弹出的结点向 $\rm LCA(u,v)$ 连边最后把 $\rm LCA(u,v)$$u$ 入栈

不难发现这样一定是对的并且设关键点数量为 $k$则虚树内点数不超过 $2k$

在一棵有 $n$ 个点的树上建立一颗有 $k$ 个点的虚树的时间复杂度为 $\Theta(k\log n)$

经典例题

[SDOI2011] 消耗战

首先有一个暴力 $\rm DP$$dp_u$ 表示将 $u$ 的子树内所有关键点与 $u$ 的联系断开所需要的最小代价转移时枚举 $u$ 的所有子节点分类讨论

  • $v$ 为关键点那么必须切断这条边$dp_u=dp_u+w_{u,v}$
  • $v$ 不为关键点那么可以切断这条边也可以从 $dp_v$ 转移故有 $dp_u=dp_u+min\{dp_v,w_{u,v}\}$

最终答案就是 $dp_1$这个暴力的时间复杂度是 $\Theta(qn)$

观察到 $\sum k\leqslant 5e5$所以考虑对每次询问建虚树

建虚树之前需要记 $M_u$ 表示从 $1$$u$ 的路径上最短的边权因为只需要切掉最短的边权就可以让点 $u$$1$ 不连通而切上面的边是更优的所以可以用 $M_u$ 直接替换转移方程中的 $w_{u,v}$

时间复杂度 $\Theta(\sum k\log n)$

[HEOI2014] 大工程

首先建虚树

  • 对于两两之间距离之和可以计算每条边的贡献
  • 对于最大$/$最小值可以树形 $\rm DP$ 或点分治

时间复杂度 $\Theta(\sum k\log n)$

[SDOI2015] 寻宝游戏

我们发现如果将虚树建出来那么答案就是虚树上所有边权之和的两倍

于是我们考虑动态维护虚树的边权之和先把所有点的 $\rm dfs$ 序求出来然后我们发现边权之和等于 $\rm dfs$ 序相邻两点的距离之和加上最左边和最右边两点的距离于是我们可以使用 $\rm set$ 维护树剖求 $\rm LCA$

时间复杂度 $\rm \Theta(n\log n)$