logn-logs 大法学习笔记
AAA 树以及其它相似复杂度分析
主要参考了 wys 的联考集团讲课。
摘要: 大法汇总。
前情提要
势能分析
对于一个数据结构 ,我们定义 表示其势能。
定义 表示对于一个数据结构第 次操作的实际代价(如线段树递归 层)
定义 为均摊代价,满足 。
那么 次操作的均摊代价总和为 。
当 时,我们可以使用 来估算时间复杂度。
简单的放缩依据
我们知道:若 ,那么 。
我们把结论的两边取 ,那么 ,即 。
为了描述清楚我们接下来的问题,我们从 Splay 开始说起。
Splay
我们对于一棵 BST,定义左旋与右旋,定义 Splay 的核心操作,splay(x)
:分三种情况,将节点 旋转到根,具体的,
- 当 的父亲为根时,执行【单旋 】。
- 当 的左右儿子属性与其父亲的左右儿子属性相同时,执行【先单旋 的父亲,再单旋 】。
- 否则(即 与父亲的左右儿子属性不同),执行【单旋 2 次 】。
我们使用势能分析证明 splay 操作的复杂度。
复杂度分析
定义 ,其中 为 Splay 的节点个数, 表示 的子树大小。
下文定义 为 完成相应操作后的子树大小。
zig step
zig-zig step
将 放缩成 ,
放缩成 ,
放缩成 。
zig-zag step
将 放缩成 ,
将 放缩成 。
综上,任意一个操作的均摊代价都被我们放缩成了 。
我们容易计算 次操作的总均摊代价:
Splay 的其他操作
注意到在 Splay 上找到一个节点 的操作次数和 splay(x)
同阶。
我们有了强大的 splay
操作后,其它操作考虑将需要处理的关键点 splay 到根处理就行了。
-
合并两个 Splay
将第一个 Splay 的最大元素 splay 至根,将第二个 Splay 作为新根的右儿子。
-
分裂 Splay
无论是按权值还是大小我们找到关键点 splay 到根,将其右儿子断掉即可。
-
打标记
类似 【子树修改】,【子树反转】的标记都是可以维护的。
LCT
LCT 不同于重链剖分对树结构固定的链剖分,由于树结构的可变,LCT 采取更灵活的实链剖分,使用 Splay 维护每条实链(Preferred Paths)。
LCT 的核心操作是 access(x)
,用来打通 到根的路径,使这条路径成为一条实链。
一个 access(l)
的例子是:
给出 access(x)
的具体流程:
重复下列操作直到 是原树的根。
- 将 旋转到所在 splay 的根(进行
splay(x)
)。 - 断掉 的右子树,将 的右子树连上上一次循环中 splay 的根。
- 将 修改为 的虚父亲。
无论是换根、加边、删边,链查询,都基于 access
操作。
我们来分析 access
的复杂度。
复杂度分析
首先,我们定义一种轻重链剖分。
对于边 是 的儿子,我们称这是一条重边当且仅当 ,否则我们称这是一条轻边。
一个显然的结论是,对于任意一个点 ,其到根的路径上最多只有 条轻边。
我们将 LCT 中的边按虚实与轻重,可以分成 4 类。
- heavy-preferred
- heavy-unpreferred
- light-preferred
- light-unpreferred
对于一次 access
操作,只有 条轻边可能由虚变实,只有 条重边可能由实变虚。
反映到 4 类边的转换上,限制是 1 到 2 单次最多 ,4 到 3 单次最多 ,一个简单的势能分析可以告诉我们均摊边的切换单次 。
边的切换次数显然也是调用 splay 的次数,由于单次 splay 均摊 ,我们可以得到 的一个上界。
我们回顾 Splay 的复杂度分析,单次 splay 操作的均摊代价是 。
这里的 是 在Splay 里的子树大小。我们改变 的定义使其依然满足 Splay 的复杂度分析,且试图让 access 时的多次 splay 均摊代价之和能邻项抵消。
具体的,我们将 的定义修正成 在 Splay 里的子树大小加上 Splay 子树内点的虚子树大小和。我们容易通过简单的放缩使得 access 的相邻两次 splay 进行抵消。
最终单次 access 的均摊代价应该是【均摊调用 splay 的次数】+【抵消后的代价:】=。
一个理解是 LCT 是一颗多叉的大 Splay。
LCT 的其他操作
-
findroot(x)
:找到 LCT 维护的森林里 所在连通块的根。考虑
access(x)
从 Splay 的根开始一直往左走到底就是根了,记得把找到的点 splay 上去保证复杂度。 -
makeroot(x)
:使 成为所在连通块的根。注意到这只会影响 和原根路径上的点的父子关系。
我们access(x)
,给 Splay 打上反转标记修改中序遍历。 -
split(x,y)
:提取 到 的路径。makeroot(x),access(y),splay(y)
-
cut(x,y)
:将边 删掉。我们
split(x,y)
,将 的父边断掉, 的左儿子断掉。 -
link(x,y)
:split(x,y)
,令 的虚父亲为 。
AAA 树呢?AAA 树呢?AAA 树呢?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix