平衡树从入门到黑题

目录
  • 基础知识
    • 注意
  • 模板水题
  • 维护序列
    • 单点操作
    • 区间操作
  • 略有难度
    • [ZJOI2006]书架
    • [SCOI2014]方伯伯的OJ
  • 写在最后

花了三周上午时间来啃平衡树。
从板子都不会写 到 独立完成黑题。
记录一下历程。


基础知识

P3369 【模板】普通平衡树

二叉搜索树

平衡树可以理解为二叉搜索树的高性能实现方式。
平衡树即为结构平衡的二叉搜索树。

Splay

通过不断将某节点旋转到根节点,使得整棵树仍满足二叉搜索树的性质。
且保持平衡不至于退化为链,以保证复杂度。

Treap

treap = tree + heap,是一种树和堆组合形成的数据结构。

treap 的每个节点上额外储存一个 关键值。
根据关键值调整 treap 结构,使其除满足二叉搜索树性质外,还满足父节点的关键值 \(\ge\) 儿子的关键值。
若关键值随机生成,则 treap 树高期望为 \(O(\log n)\)

Fhq-Treap

又称无旋 treap, 是一种特殊的 treap。
以分裂和合并为核心。
其操作方式使它天生支持维护序列、可持久化等特性。


注意

一棵平衡树最重要的是它的结构。
只有结构才能反映 其维护的信息。
节点中携带的信息不一定都与其结构有关。

在实际应用时 一定要明确结构维护的信息。


模板水题

用于练习板子和刷AC数。

P1486 [NOI2004]郁闷的出纳员 查询第k大
P1801 黑匣子 查询最值
P2073 送花 查询最值
P2234 [HNOI2002]营业额统计 查询前驱后继
P2286 [HNOI2004]宠物收养场 查询前驱后继
P2343 宝石管理系统 查询最值
P3871 [TJOI2010]中位数 维护中位数 (查询指定排名)
P3850 [TJOI2007]书架 查询第k大


维护序列

平衡树结构 维护权值在序列中的排名。
则可使 中序遍历 = 原序列。

单点操作

一道水题 P3850 [TJOI2007]书架:
单点插入,查询指定排名元素。

现有一数列 1 9 2 6 0 8 1' 7

平衡树从入门到黑题_第1张图片

如图,图中节点上的数 为权值。
平衡树结构 维护权值在序列中的排名。
将平衡树中序遍历后 即可得原序列。

查询操作 即为基础的 查询第k大操作。
根据二叉树搜索树左小右大的性质 和 维护的子树大小可得答案。

  • 若左子树非空 且 剩余排名 \(rank\) 不大于左子树的 Size,则向左子树查找。
  • 否则将 \(rank\) 减去左子树的和 当前根的大小。
    如果此时\(rank\)的值小于等于 0,则返回当前根节点的权值,否则继续向右子树查找。

将w插入至k位置,即将w置于 原排名为k的权值 的前面,成为它的前驱。
在平衡树中,即令w成为 原排名为k的权值为根的子树 左子树中最右的结点。

若使用Fhq-Treap,直接按子树大小分裂即可。

代码详见 P3850 [TJOI2007]书架 - Luckyblock。

区间操作

P3391 【模板】文艺平衡树 区间翻转

使被操作序列置于一棵完整的子树中。
通过在 子树根打标记实现区间操作。
Splay 和 Treap 将序列旋转至一棵子树中,
Fhq-Treap 将序列分裂至一棵子树中。

代码详见 P3391 【模板】文艺平衡树 - Luckyblock。


略有难度

[ZJOI2006]书架

给定一书列,从 \(1\sim n\) 依次编号。
给定下列五种操作 :
\(\text{Top}\ S\) :将编号S的书 放在最上面。
\(\text{Bottom}\ S\) :将编号S的书 放在最下面。
\(\text{Insert}\ S\ T,T\in[-1,1]\):将编号为S的书 与 编号为S+T的书交换位置。
\(\text{Ask}\ S\) :询问编号为S的书的排名。
\(\text{Query}\ S\):询问排名为S的书的编号。
\(n,m \le 80000\)

乍一眼看上去和 水题 P3850 [TJOI2007]书架 神似。
但水题只有排名和书名的单向查询,本题有排名和编号的双向查询,以及奇怪的修改操作。

则可选择建立映射关系:
每一本书 都对应 一个节点。
节点信息中储存 书的编号。
position[i] 维护 编号为 i 的书 对应的的节点编号。

同 [TJOI2007]书架,平衡树结构 维护在序列中的排名。

对于两种查询操作:

  • 查询排名:将编号为 S 的书 对应的的节点旋转至根,输出其左子树大小。
  • 查询权值:经典查询第k大操作。

置顶操作

  • 可将原序列分为三份:[1,S-1], [S], [S+1,n]。
    手玩后发现,置顶S即将原序列变为 [S], [1,S-1], [S+1,n]。
    [1, S-1] 与 [S+1,n] 内部排列顺序不变。
  • 由此考虑置顶S后 树的结构的变化。
    将 S 旋转至根,则其左子树为 [1, S-1] ,右子树为[S + 1, n]。
    由[1, S-1] 与 [S+1,n] 内部排列顺序不变,则左右子树内部结构不变,可将其看作两节点。
    平衡树从入门到黑题_第2张图片 \(\Longrightarrow\) 平衡树从入门到黑题_第3张图片

    显然可以通过 将[1,S-1] 变为 [S+1,n] 的左子树,来实现[S]的置顶。
  • 再将 [1,S-1] 与 [S+1,n] 看作两子树。
    可使[1, S-1] 成为[S+1, n] 中排名最小的节点 (即最左的节点) 的左子树。
    -此操作只改变平衡树结构,不改变 包括position的各种维护的信息。

置底操作

  • 原理与置顶操作相同。
    将S旋转至根,使右子树成为 左子树中最右节点 即可。

相邻元素交换操作

  • 直接与该元素的前驱/后继交换 位置及信息。
    注意更新 poistion 的值。

代码详见 P2596 [ZJOI2006]书架 - Luckyblock。


[SCOI2014]方伯伯的OJ

给定一初始长度为n的序列,其中元素从 \(1\sim n\) 依次编号。
给定下列五种操作 :
\(\text{1}\ x\ y\) :将编号为x的元素 编号改为 y,输出该元素在序列中的排名。
\(\text{2}\ x\) :将编号x的元素 置顶,输出执行该操作前编号为x元素的排名。
\(\text{3}\ x\) :将编号x的元素 置底,,输出执行该操作前编号为x元素的排名。
\(\text{4}\ x\):询问排名为x的元素的编号。
强制在线
\(1\le n\le 10^8, 1\le m \le 10^5, 1 \le y \le 2 \times 10^8\)

?这和上一道不是一个题
?怎么就变成黑题了
!看我秒切黑题
\(\text{Memory Limit Exceeded}\)

请勿混淆 排名,编号,节点编号 三者的概念。
排名 为 某序列元素 在 序列中的次序。
编号/元素编号 意义与题面中"编号"意义相同,为 某序列元素被标记的号码。
节点编号 为 某平衡树节点 的 node_num,在插入新节点时被赋予,恒定不变。

基本思想

  • 与 上题 [ZJOI2006]书架 一致,均是建立映射关系,维护元素编号 对应的 节点编号。
    但数据范围巨大,不可直接套用做法。

  • 发现有许多元素根本访问不到,考虑动态开点,
    将一个连续的区间 压缩成一个节点。

struct SplayNode {
	int son[2], fa, size, cnt; //cnt区间长度
	int l, r; //l,r 为区间左右端点。
  • 形态如下,中序遍历仍然是原序列。

    平衡树从入门到黑题_第4张图片
  • 开两棵动态开点平衡树。

    • 一棵平衡树 t 用于查询排名,回答询问。
      压缩 元素编号。
      其结构 维护在序列中的排名。
      其中序遍历 即为给定序列。
    • 一棵平衡树 t1 用于建立 元素编号与 t中节点编号 的映射关系。
      其结构 维护元素编号, 其中序遍历 必为 1~ n。
      节点储存 其压缩的元素编号 在 t 中的节点编号。

操作1

  • 如果被操作节点还在 一区间节点中,将其在两棵树中都分裂出来。

    平衡树从入门到黑题_第5张图片$\Longrightarrow$ 平衡树从入门到黑题_第6张图片
  • 先在 t1 中找到 x 在 t 中对应的节点编号。
    回到 t 中,将对应节点的 l,r值替换为 y。

  • 将 编号x在 t1中对应节点删除,插入编号y的新节点。 使新节点与原节点 t1中节点编号一致。
    注意维护结构,保证中序遍历不变。

  • 查询排名时,将t中对应节点旋转至根。
    输出其左子树大小 + 1。

操作 2,3

  • 先分裂出对应节点,在t中将其旋转至根。
    需要输出的查询排名 即为 左子树大小 + 1。

  • 操作方式与 上题相同。操作2将左子树挂到右子树最左侧。 操作3将右子树挂到左子树最右侧。

  • 注意特判 根有无左右子树。

操作 4

  • 经典查询 第k大值操作。
    直接在 t 中按照排名查询即可。

  • 具体分裂方式 详见代码。
  • t1 功能单一可使用map替代,详见锣鼓题解。太懒了就没写。

代码详见 P3285 [SCOI2014]方伯伯的OJ - Luckyblock。


写在最后

啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 好想看秘封组贴贴啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 平衡树从入门到黑题_第7张图片

你可能感兴趣的:(平衡树从入门到黑题)