关于使用 Fhq Treap 维护序列的小研究
前言
众所周知, 简单)的题目来说明。
宏定义一些变量:
$R_x$: $x$ 的右儿子。 $id_x$: $x$ 在序列中的编号。 $rank_x$:$x$ 在其所在树中的排名。 $T_L$ 与 $T_R$: 分裂出的左右树。 $root_x$:树 $x$ 的根。 $siz_x$:以 $x$ 为根的子树的大小。
按排名分裂,单点维护序列
维护的
- 每个节点代表序列中的一位,且节点按
id 排名。
则有推论:
-
id_x = rank_x -
重点:
可以类比普通平衡树的维护方法,这两者之间的区别几乎仅在于分裂的方法不同。
P3850 [TJOI2007]书架
一句话题意:
维护序列,支持:在
pos 位插入一个元素,查询第pos 位上的元素。
这是一道使用
- 对于第一种操作,按照普通平衡树插入节点的方法去处理,只不过改成了按排名分裂。
- 对于第二种操作,直接查询
kth 即可。
P3391 【模板】文艺平衡树
一句话题意:
维护序列,支持:区间反转,查询当前序列
使用一个节点去维护序列上的一位,瓶颈在于怎么一次性拿出一个区间。
按照普通平衡树的套路,假设按
首先
然后在右树上
那么使用维护序列时拿出区间
那么:
- 初始时可以使用类似线段树的方法建树,有如下代码:
int build(int l, int r) {
if(l > r) return 0;
int o = ++ tot; id[o] = mid;
L[o] = build(l, mid - 1);
R[o] = build(mid + 1, r);
return 0;
}
- 对于第一种操作,拿出区间
[l,r] 后, 在根节点打上reverse 的标记,在push\_down 的时候交换左右儿子,再给左右儿子打上reverse 标记即可。
感性理解: 全树交换后所有大小关系都将反转,则全树的
- 对于第二种操作,中序遍历全树即可,性质在此:
P4309 [TJOI2013]最长上升子序列
一句话题意:
依次插入从
1 到n 的数字至指定位置,每次插入求全局最长上升子序列的长度
一道简单的应用题,因为插入的数是单调递增的,所以新插入的数可以跟左侧的任何位置组成上升子序列。
每个节点维护信息
如果在第
新插入的数可以跟左侧的任何位置组成上升子序列。
那么从左侧选择最长的上升子序列与其组成上升子序列,则这个上升子序列就是以
易知:左侧选择最长的上升子序列的长度即为左树根的
得到
按排名分裂,维护序列连续段
显然一个节点代表一个位置的效率过低,考虑以一个节点维护一个连续段的方式维护序列。
维护的
- 每个节点代表序列中的一段
[l,r] ,大小关系为:
重定义
如果对于每个节点维护其区间长度
即为子树维护区间的长度,这样类比按排名分裂,单点维护序列的维护方法,好像有了一些共同点。
按照这样的
-
l=rank_x,r =rank_x + len_x-1
重点:
可以类比按排名分裂,单点维护序列的维护方法,并需要一定的自主思考能力。
使用
按左端点分裂,将左端点大于
那么还需要考虑一种情况,就是线段
这种线段
那么我们得到的这个树就维护了区间
一种节约内存的做法:
对于一些题目,我们可以只维护部分序列信息,如:维护
那么我们可以对于每个区间记录
如果需要插入时我们可以打上
例题:[Ynoi2014] 人人本着正义之名