浅谈 LCA
LCA(
Least Common Ancestors
),最近公共祖先,定义为两节点最近的公共祖先好像是废话
前置芝士:
- 图论
此文章中均设 为 的父亲, 为 的深度。
暴力
显然我们找出节点的所有祖先再 比较即可。
当然你也可以一层层往上跳。
时间复杂度是 。
倍增
我们思考一次性跳多步,减少时间复杂度。
考虑二进制拆分。
考虑用 dp 预处理:
设 为 往上跳 步到达的点,即可以使用以下转移方程:
所以我们设 ,即 直接从 往下枚举即可。
整理下,如果我们求 :
默认 (如果不满足根据 交换 即可)
过程如下:
- 预处理 数组
- 将 跳至同一层
- 如果相等直接返回
- 否则继续跳,直到它们都跳到 LCA 的往下一层。
这个在链上极其好用。
代码:
int LCA(int u,int v)
{
if (dep[u]<dep[v]) swap[u][v]; //交换
while (dep[u]>dep[v]) //预处理
u=fa[u][lg2[dep[u]-dep[v]-1]];
if (u==v) return u; //跳出
for (int i=lg2[dep[u]-1];i>=0;--i)
if (fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i]; //继续跳
return fa[u][0];
}
RMQ求解
RMQ(
Range [Minimum/Maximum] Query
),区间最值问题。
首先我们要了解一个离线求 RMQ 的数据结构——st表(Sparse Table
)
因为 st 表也是倍增思想,所以转移方程也会很像:
-
设 表示 开始, 个元素的最值(不一定连续)
-
则 (切开求解)
首先考虑最值允许区间重叠:
我们即肯定能找到一个 使得 和 的最值与 的最值相等。
则这个 很容易想出( 之间有 个元素):
我们回归 LCA。
首先我们要了解欧拉序。
以此图为例:
设 为树根:
则它的属性:
- DFS 序:;
- 带上回溯的 DFS 序:。
其中“带上回溯的 DFS 序”即为欧拉序。
我们看看此图:
比如我们找 的 LCA:
写出欧拉序:
转换为深度:
找出 之间区域:
正好深度最低的点就是 ,它们的 LCA!
这样就把 LCA 转换为了 RMQ,st表求解即可。
Tarjan 算法
我们引用 rxz 的话:
一个熊孩子 Link 从一颗有根树的最左下节点灌岩浆,Link 表示很讨厌这种倒着长的树,岩浆会不断蔓延到整个树。
如果岩浆灌满了一颗子树 Link 发现树的右边有棵更深的子树,则 Link 会去灌岩浆。
岩浆只有迫不得已的情况才会升高,找新子树进行注入。
机(yu)智(chun)的 Link 发现了一个求 LCA 的好办法,即:如果两个节点都被岩浆烧掉时,它们的 LCA 即为那棵子树上岩浆最高的位置。
即按 rxz 描述的写即可,伪代码如下:
void tarjan()
{
for (u的所有儿子v)
{
tarjan(v);
merge(u,v); //并查集
}
for (所有与u有关的查询(u,v))
if (vis[v]) ans[id]=find(v);
}
树剖写法
树剖,即树链剖分,将树变为链的方法,可以应对某些
毒瘤出题人将数列上问题转移到树上的情况。
我们求 LCA 用的是轻重链剖分,也就是将树变成轻链和重链。
我们首先给出一些定义:
- 重儿子:某节点儿子中子树最大的儿子(相等随便选一个)
- 轻儿子:除重儿子以外的所有儿子
- 重边:爹连到重儿子的边(爹不一定是重儿子)
- 轻边:除重边外所有边
- 重链:重边组成的链(轻叶节点自成重链)
- 轻链:轻边组成的链
我们树剖需要的数组:
- 表示以 为根的子树大小。
- 表示 节点的重儿子。
- 表示 所在的重链头(深度最浅节点)。
树刨和莫队等等一样都是优雅的暴力 ,会被轻重链交替的数据或者全是轻链的数据卡死。
首先两次 DFS:
- 第一次求 、、、。
- 第二次只求 。
然后轻重链交替跳 LCA 即可(适时原 地 踏 步)。
以下是博客签名,正文无关
本文来自博客园,作者:yspm,转载请注明原文链接:https://www.cnblogs.com/CDOI-24374/p/12820809.html
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0)进行许可。看完如果觉得有用请点个赞吧 QwQ
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】