P3835

发现不用指针写的FHQ-Treap题解似乎被撤了,来一发吧

0.替罪羊树

单纯记录一下代码


1.FHQ-treap

不会写treap的左转P3369 【模板】普通平衡树,这里强烈推荐这篇题解
FHQ Treap 的优点包括好理解,上手快,代码一般很短,可持久化等。
与一般Splay最大的不同就是它是无旋的。
啥意思呢?
就是说,通过拆树和合并代替Splay的旋转操作。
具体怎么实现呢?

1.1 插入

首先,把树分为左半树和右半树,然后暴力插
这里随便找个例子

  3
 / \
 2 6
/  /\
1 5 7

比如,我现在要把4插进去。
直接调用一下,嘿!4比3大,比6小,那不就得了
所以变成这样两棵树

3
/  
2 6
/ /\
1 5 7

再把4塞到左树的右子节点上,送他一个右儿子6

3
/\
2 4
/ \
1 6
  /\
  5 7

懂了吧
其中找位置 \(O(logn)\),插入 \(O(1)\),总计 \(O(logn)\)

1.2 删除

同理可得,把插入的操作反过来就行
具体操作,就是把它删了,它的左子树并到它的父节点,再把右子树并到左子树上
还是这个树

3
/\
2 4
/ \
1 6
  /\
  5 7

现在我要把6删了
则先暴力删6

3
/\
2 4
/ 
1 

  5 7(注意这里是两棵树)

再分别把5,7接上去

3
/\
2 4
/ /
1 5
  \
  7

显然 \(O(1)\),是操作中最快的。

1.3 查询

一个一个说
通常来说,使用分裂和合并来实现更加简洁,但是速度会慢一点。
我喜欢快,所以代码会使用一般treap的查找方式。

1.3.1 查询 M 中有多少个数比 \(x\) 小,并且将得到的答案加一。

显然,找第一个小于 \(x\) 的树即可。
实现时可查询第一个不大于 \(x-1\) 的数,复杂度 \(O(logn)\)

1.3.2 查询如果将 M 从小到大排列后,排名位于第 \(x\) 位的数。

记录size,比它大就往左,比它小就往右
复杂度 \(O(logn)\)

1.3.3 查询 M 中 \(x\) 的前驱/后继

一起说。
前驱:找左子树的最右子节点
后继:找右子树的最左子节点
直接套1.3.2即可

1.4复杂度分析

空间复杂度,至多 \(n\) 个节点,故 \(O(n)\)
时间复杂度,每次查询至多 \(O(logn)\),整体共 \(O(nlogn)\),可以通过

2.可持久化FHQ-treap

与其他可持久化数据结构类似。
核心思想是:尽可能新建更少的结点记录新版本的信息,同时保留住历史版本的结点信息。
所以每更改一个结点就要分值一个新的结点出来,否则你修改的就是历史版本上的结点。
由于本题不强制在线,可直接套FHQ-treap。

3.代码

点击查看代码

4.结语

两年半的算法吟
by yzc001

两年半光阴飞逝去,
初窥门径亦懵懂。
屏幕代码夜星稀,
Bug 缠身心彷徨。

搜索剪枝如履冰,
动态规划似迷宫。
AC 喜悦胜琼浆,
WA 苦涩泪沾裳。

从数组到线段树,
从模拟到最短路。
算法思维渐清晰,
编程能力日臻健。

曾记赛场手心汗,
也曾深夜挑灯干。
失败教训铭于心,
成功喜悦更勇敢。

莫道信息路漫漫,
坚持不懈终登攀。
OIer们多努力,
NOI金牌等着你!

posted @ 2025-06-07 17:57  yzc_is_SadBee  阅读(11)  评论(0)    收藏  举报