splay区间操作

splay是平衡树的一种,但其更广泛的应用是进行对区间的操作,在熟悉了splay平衡树的一系列操作之后,就可以开始探究其区间操作了。

splay运用在区间操作上时,每个结点都对应区间上的一个点,每个子树都对应一段区间。而splay可以当作区间树的根本原因是“旋转操作不改变splay的中序遍历序列”,因此要对一段区间进行操作时,只需要通过一系列旋转,使某个子树对应该段区间即可。

这时候,满足二叉搜索树性质的是该结点对应的点在区间中的位置,但这个数据并不需要储存在splay树上。
首先考虑如何将一段区间对应到一个子树上。
如果要对区间 [ L , R ] [L,R] [L,R]进行操作,考虑先将对应 L − 1 L-1 L1的结点旋转到根,再将对应 R + 1 R+1 R+1的结点旋转到根的右子结点,由于区间树是将区间的中序遍历顺序当作对应的结点在区间内的顺序,这时对应 R + 1 R+1 R+1的结点的左子结点为根的子树对应的就是区间 [ L , R ] [L,R] [L,R]

从上述操作可以发现,如果操作的区间是 [ 1 , k ] [1,k] [1,k]或者 [ k , N ] [k,N] [k,N],上述操作所要寻找的 L − 1 L-1 L1结点或 R + 1 R+1 R+1结点可能不存在,因此如果可能会调用到 0 0 0结点的话需要插入一个哨兵结点,可能调用 N + 1 N+1 N+1结点也是同理在尾部插入哨兵结点。但是某些题目中,可能只会调用 0 0 0结点而不调用 N + 1 N+1 N+1结点,而又有向区间尾部合并的操作,这时在尾部插入了哨兵结点就可能会出现BUG,所以插入哨兵结点与否也要具体问题具体分析。

已经知道如何对应到区间上之后,就可以开始考虑一些基本的操作了。
对于区间翻转操作,完成了区间选取操作后可以给对应子树的根节点打上标记表示翻转操作,在输出或者旋转时swap左右子节点即可。
对于原splay的合并操作,如果子树B的最小值大于等于子树A的最大值,就可以将B合并到A上。但对于区间操作来说就没有这些限制,原来的合并操作等价于区间B合并到区间A之后。
分裂操作也是同理。
而对于区间最小值,区间增加一个数,可以类似线段树,在结点上记录数据并添加懒标记。

例如:POJ3580
题意:
共有六种操作:
①区间 [ L , R ] [L,R] [L,R]上均添加一个数 x x x
②翻转区间 [ L , R ] [L,R] [L,R]
③旋转区间 [ L , R ] [L,R] [L,R]
④在第 x x x个点后面插入值 p p p
⑤删除第 x x x个点
⑥求区间 [ L , R ] [L,R] [L,R]的最小值

AC代码

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
#define rep(i,s,t) for(int i=(s);i<=(t);i++)
#define per(i,s,t) for(int i=(s);i>=(t);i--)
#define ms0(ar) memset(ar,0,sizeof ar)
#define ms1(ar) memset(ar,-1,sizeof ar)
#define Ri(x) scanf("%d"
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值