树链剖分
P3379 【模板】最近公共祖先(LCA)
本题中的树链剖分均指重链剖分。
这里不使用重链剖分作为模板是因为这道题更加典型,不需要使用额外的数据结构,就是纯天然、无污染的树剖(我诗兴大发喝多了)。
首先,树剖是一个思想,可以将树上两点路径的问题转变为一个序列上,不超过
切入正题,树剖到底是什么呢。
直接上图
重子节点为其父节点的子节点中中子树大小最大的节点(有一样的选一个即可),轻子节点为除其父节点的重子节点外的其他子节点。重边指下面连接重子节点的边。轻边则为除重子节点外的边。
重链就是由若干段连续重边组成的路径。
然后我们再做一遍 dfs
,优先遍历每个点的重子节点,然后可以得到右边图的 DFN
序。这样遍历保证了每段重链上的点的编号均连续。
又有一个定理:对于树上任意一条路径,都能拆分成
因为你只要走一条轻边,那么子树大小至少减小一半,而每进入一条重链都需要走一条轻边,所以就是
对于本题,尽管求的是 LCA
,我们一般用朴素的倍增算法求解,但是也可以使用树链剖分(常数更小)。
对于两个点,每次优先让所在重链顶端的点深度更深者跳到重链顶端的父亲。然后直到两者在一条链上停止。最终深度较小者为 LCA
。
简单证明的话,只要两个点不在一个重链上,那么肯定没有到达 LCA
,而一旦汇合,那么这个点肯定是第一个汇合的点(因为本来在两条链上,你突然有一个点跳到了另外这条链上,那么第一个汇合的位置就是这里了)。
尽管代码稍微比倍增写法长,但是好写常数小。
代码
本文作者:wscqwq
本文链接:https://www.cnblogs.com/wscqwq/p/17517881.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步