虚树 Virtual Tree
更新日志
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}\),说明它是当前节点的一个兄弟节点:
最后剩下的链里的节点依次连边,虚树就构建好了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?