序列之王-Splay的应用

本文探讨了Splay树如何进行区间操作,包括区间增删、分裂与合并等,介绍了Splay树在处理区间ADD、REVERSE、REVOLVE、INSERT、DELETE等操作时的方法。通过两个实例题目的解析,详细阐述了Splay树在处理记忆游戏中数列变化的高效策略,以及在空间限制下如何优化实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Splay进行区间操作的原理就在于,其使用排名来构成区间下标,这样可以适应区间的增删
比如提取出区间 [ l , r ] [l,r] [l,r],我们仅需要将排名为 l − 1 l-1 l1的节点旋转至根,再将排名为 r + 1 r+1 r+1的节点旋转至根的右儿子,此时根节点的右儿子的左儿子便是整个区间 [ l , r ] [l,r] [l,r],当然在实际应用中有哨兵这个恶心的玩意,于是我们需要对查询的排名进行 + 1 +1 +1
对于区间操作的Splay有一个好的建树方法,也即我们将 a [ 1 ] , a [ n + 2 ] a[1],a[n+2] a[1],a[n+2](a表示原序列)设为哨兵,至于哨兵的值是0/-inf/inf需要根据具体情况而定,然后运用线段树的建树方式,建立出一个Splay,然后每次区间操作的时候都将 l , r l,r l,r加上1
当然,Splay也有懒标记的扩展,但需要注意的是,对于懒标记的下传,一般有两种形式,一种是使用find函数下传,一种是在Splay函数中下传,这里我们使用find下传
对于Splay维护区间操作的核心函数在于两个:spilt and merge
spilt 分裂,merge合并

void splay(int x,int goal){//这里小小偷个懒
	goal=f(goal);
	while(f(x)!=goal){                                                                                
		int y=f(x),z=f(y);
		if(z!=goal){
			rc(y)==x^rc(z)==y?rotate(x):rotate(y);
		}
		rotate(x);
	}
	if(!goal)rt=x;
}
int find(int x,int k){ // 查询x的子树上第k大 ,递归写法
	if(!x) return 0;
	pushdown(x);
	int s=siz(ch(x,0))+1;
	if(s==k) return x;
	else if(s>k) return find(ch(x,0),k);
	else return find(ch(x,1),k-s);
}
void spilt(int x,int k,int &a,int &b){//将x中前k个分在a上,将其他的分在b上
	a=find(x,k);
	splay(a,x);
	pushdown(a);//注意这里需要pushdown
	b=rc(a);
	rc(a)=0,f(b)=0;
	pushup(a); 
}
int merge(int a,int b){//将b合并在a上
	int pos=find(a,siz(a));
	splay(pos,a);
	rc(pos)=b; 
	f(b)=pos;
	pushup(pos);
	return pos;
}//注意全部用pos

下面我们来两道例题感受一下Splay的强大,一般考察的区间操作也就这些了

超级备忘录

题目链接
描述
你的朋友达达被邀请参加一个叫做“超级备忘录”的电视节目。

在这个节目中,参与者需要玩一个记忆游戏。

在一开始,主持人会告诉所有参与者一个数列, A 1 , A 2 , … , A n A1,A2,…,An A1,A2,,An

接下来,主持人会在数列上做一些操作,操作包括以下几种:

ADD x y D:给子序列 {Ax,…,Ay} 统一加上一个数 D。例如,在 {1,2,3,4,5} 上进行操作ADD 2 4 1 会得到 {1,3,4,5,5}。
REVERSE x y:将子序列 {Ax,…,Ay} 逆序排布。例如,在 {1,2,3,4,5} 上进行操作 REVERSE 2 4 会得到 {1,4,3,2,5}。
REVOLVE x y T:将子序列 {Ax,…,Ay} 轮换 T 次。例如,在 {1,2,3,4,5} 上进行操作 RE

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值