更新日志
2025/01/07:开工。
概念
在很多树上问题中,我们会发现,实际需要的,只有几个关键点。
那么我们就可以针对这些关键点进行操作。更具体地,建一棵规模更小的,但是仍能完成要求的浓缩过的树,即为虚树。
思路
简介
首先,常识可得:除了关键点,关键点两两的 \(\text{LCA}\) 也需要储存重要信息。
所以建立虚树的关键问题就是:找 \(\text{LCA}\)、按原关系建树。
下面介绍两种构造方案。
二次排序
\(O(m\log n)\),\(m\) 为关键点数。
大概过程就是:
- 关键点按 \(\text{DFS}\) 序排序。
- 相邻两点求出 \(\text{LCA}\),加入序列。完成后去重,序列中的节点就是虚树中的节点了。
- 对于每两个相邻点 \(x,y\),连接 \(\mathrm{LCA}(x,y)\) 和 \(y\)。
下面证明第二和第三过程。
首先,可以想象,按 \(DFS\) 序排序后,相邻的节点要么是祖先关系、要么是兄弟关系(很废话),且从左向右排开。假如二者跨了子树——总会有这样的点——就会把两棵子树的父节点加入,而它也是任意左子树点和任意右子树点的 \(LCA\)。感性理解即可。
然后,我们分讨一下第三过程:
- \(x,y\) 为祖先关系,那么直接连边显然无问题,且二者之间没有别的关键点了。
- \(x,y\) 非祖先关系,\(\text{LCA}(x,y)\) 显然也应该是 \(y\) 的祖先。且二者之间显然也没有别的关键点了,因为 \(x,y\) 在 \(\text{DFS}\) 序上相邻。可以大概想象一下。
- 发现第一个点没有连边,但是作为 \(\text{DFS}\) 序最小的,它必然是根节点,所以无需主动连边。
证毕。不难发现虚点个数为实点个数两倍。
单调栈
类似于笛卡尔树之类的,我们考虑维护虚树上的一条链。
首先,我们把原根节点加入进去,确保正确性。
我们考虑加入一个节点的情况:
- 若栈顶节点是新加入节点的祖先,直接入栈。
- 否则:
- 若栈顶元素 \(\text{DFS}\) 序大于 \(\text{LCA}\),说明它是当前节点的一个兄弟节点:
- 若次大元素仍大于 \(\text{LCA}\),将栈顶元素与次大元素连边,弹出。
- 否则,说明当前栈顶元素的父节点应为 \(\text{LCA}\),连边,弹出。
- 若栈顶元素 \(\text{DFS}\) 序小于 \(\text{LCA}\),说明此时 \(\text{LCA}\) 还没入栈,将其加入后,再把当前节点加入即可。
- 若栈顶元素 \(\text{DFS}\) 序大于 \(\text{LCA}\),说明它是当前节点的一个兄弟节点:
最后剩下的链里的节点依次连边,虚树就构建好了。