平衡树(术)之splay

有人能告诉我spaly是什么吗

二叉搜索树

保证对于一个点 x x x.
v a l [ x ] &gt; = v a l [ l s o n [ x ] ] val[x] &gt;= val[lson[x]] val[x]>=val[lson[x]] v a l [ x ] &lt; = v a l [ r s o n [ x ] ] val[x]&lt;=val[rson[x]] val[x]<=val[rson[x]]

定义

s o n son son儿子节点
f a fa fa父亲节点
v a l val val节点权值
c n t cnt cnt权值的个数
s i z e size size左右儿子的 s i z e + size+ size+自己的 c n t cnt cnt

一些基础操作

返回是否是右儿子.
在这里插入图片描述
更新信息
在这里插入图片描述
找到权值为 x x x的点并将其旋到根
在这里插入图片描述

插入操作
就依照二叉搜索树的性质来
在这里插入图片描述
查找 x x x的排名
在这里插入图片描述
查找排名第 x x x的数
在这里插入图片描述
查找 x x x的前驱或后继(0为前驱1为后继)
在这里插入图片描述

一些进阶操作

1. r o t a t e rotate rotate
分左旋和右旋
贴一张 盗的 图上来
在这里插入图片描述
自己看吧…
在这里插入图片描述
2. s p l a y splay splay
s p l a y ( x , y ) splay(x,y) splay(x,y)指将 x x x旋到 y y y的儿子.
具体过程还不是很会,贴代码.
在这里插入图片描述
3. d e l e t e delete delete
找到 x x x的前驱和后继,然后利用后继的性质删除即可.
在这里插入图片描述

用平衡树处理更多东西

以上所有的操作都是基础操作.
如果要维护一段区间,又怎么用 s p l a y 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的左子树即为满足要求的区间.
随后在 r + 1 r+1 r+1的左子树打一个标记即可.
当然在 s p l a y splay splay的时候要下传一下当前 s p l a y splay splay节点的标记.
tips:插入一个 − I N F -INF INF I N F INF INF作为 1 1 1号节点和 n + 2 n+2 n+2号节点,会使代码好写.

代码(Luogu3369普通平衡树)

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e5 + 5,INF = 0x3f3f3f3f;

struct Splay
{
	int fa,val,size,cnt;
	int son[2];
}tr[N];

int rt,tot,n,opt,x;

int get(int x)
{
	return tr[tr[x].fa].son[1] == x;
}

void update(int x)
{
	tr[x].size = tr[x].cnt + tr[tr[x].son[0]].size + tr[tr[x].son[1]].size;
}

void rotate(int x)
{
	int y = tr[x].fa,z = tr[y].fa,kind = !get(x);
	tr[y].son[!kind] = tr[x].son[kind];
	if (tr[x].son[kind]) tr[tr[x].son[kind]].fa = y;
	tr[x].fa = z;
	if (z) tr[z].son[get(y)] = x;
	tr[x].son[kind] = y,tr[y].fa = x;
	update(y),update(x);
}

void splay(int x,int anc)
{
	while (tr[x].fa != anc)
	{
		int y = tr[x].fa;
		if (tr[y].fa == anc) rotate(x);
		else rotate(get(x) == get(y) ? y : x);
	}
	if (!anc) rt = x;
}

void insert(int x,int now)
{
	int fa = 0;
	while (now && tr[now].val != x) { fa = now,now = tr[now].son[x > tr[now].val]; }
	if (now) ++tr[now].cnt;
	else
	{
		now = ++tot;
		tr[now].size = tr[now].cnt = 1,tr[now].val = x,tr[now].fa = fa;
		if (fa) tr[fa].son[x > tr[fa].val] = now;
	}
	splay(now,0);
}

void find(int x)
{
	if (!rt) return;
	int now = rt;
	while (tr[now].son[x > tr[now].val] && tr[now].val != x) now = tr[now].son[x > tr[now].val];
	splay(now,0);
}

int Rank(int x)
{
	find(x);
	return tr[tr[rt].son[0]].size;
}

int kth(int x)
{
	if (x > tr[rt].size) return 0;
	int now = rt,pos;
	while (1)
	{
		pos = tr[now].son[0];
		if (tr[pos].size + tr[now].cnt < x) 
		{
			x -= tr[pos].size + tr[now].cnt;
			now = tr[now].son[1];
		}
		else if (tr[pos].size >= x) now = pos;
		else return tr[now].val;
	}
}

int pre_suf(int x,int flag)
{
	find(x);
	if ((tr[rt].val > x && flag) || (tr[rt].val < x && !flag)) return rt;
	int now = tr[rt].son[flag];
	while (tr[now].son[!flag]) now = tr[now].son[!flag];
	return now;
}

void del(int x)
{
	int pre = pre_suf(x,0),suf = pre_suf(x,1);
	splay(pre,0),splay(suf,pre);
	int now = tr[suf].son[0];
	if (tr[now].cnt > 1) --tr[now].cnt,splay(now,0);
	else tr[suf].son[0] = 0;
}

int main()
{
	scanf("%d",&n);
	insert(-INF,rt),insert(INF,rt);
	while (n--)
	{
		scanf("%d%d",&opt,&x);
		switch (opt)
		{
			case 1:insert(x,rt); break;
			case 2:del(x); break;
			case 3:printf("%d\n",Rank(x)); break;
			case 4:printf("%d\n",kth(x + 1)); break;
			case 5:printf("%d\n",tr[pre_suf(x,0)].val); break;
			case 6:printf("%d\n",tr[pre_suf(x,1)].val); break;
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值