虚树学习笔记
虚树,是一种针对树上点集的强力运算。它可以在(其中是点集大小)的时间内,建出一棵包含点集中所有节点,以及其中某些点的lca的树出来。这棵树就被称作虚树。之后就可以在虚树上进行操作了,例如树形DP等。
建出虚树的操作主要是这样的:
-
我们维护一个栈,从栈顶到栈底构成原树上一条深度递减的链。注意这条链不是完整的链——它只记录了链上的关键节点。
-
我们首先将原树的根节点加入虚树,方便维护。接着,将点集中所有点按照dfs序排序,然后依次加入虚树。
-
当一个点被加入虚树时,如果栈为空,直接将该点加入栈,并直接结束函数;否则,求出它与栈顶节点的LCA。
-
如果栈中第二个元素的深度小于LCA的深度,在虚树上连一条边,并弹出栈顶节点。重复此操作,直到第二个元素的深度大于等于LCA的深度。
-
如果栈顶元素的深度小于LCA的深度,连一条边,并弹出栈顶元素。
-
如果栈顶元素不同于LCA,将LCA入栈。
-
将(3)中那个点入栈,并结束该函数。
-
在所有东西全部加入虚树后,清空栈,在清空的过程中同时连边。
代码:
void ins(int x){
if(!tp){stk[++tp]=x;return;}
int lca=LCA(x,stk[tp]);
while(tp>=2&&dep[lca]<dep[stk[tp-1]])v[stk[tp-1]].push_back(stk[tp]),tp--;
if(tp&&dep[lca]<dep[stk[tp]])v[lca].push_back(stk[tp--]);
if(!tp||stk[tp]!=lca)stk[++tp]=lca;
stk[++tp]=x;
}
理解:
因为我们事先将节点按照dfs序排序,所以当新节点的深度大于栈顶节点深度的时候,就意味着原树中栈顶节点子树内所有节点都已经被加入虚树,可以弹出了。
然后,这个lca为了维护虚树的形态,也应该被加入虚树。
总复杂度,瓶颈在于按照dfs序排序。
一些定义:
虚树:即上文代码所建出的树
原树/实树:原本的树
实点:给出点集中的点
虚点:在建出虚树时为了维护形态而添加的点(即实点的LCA之类)
树外点:原树中不在虚树内的点
树点:虚树内的点(大部分没有歧义的时候,“节点”就特指树点)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?