P3369 【模板】普通平衡树【Splay】

这篇博客详细介绍了如何使用Splay树这种数据结构来执行插入、删除、查询排名、查找特定排名的数、寻找前驱和后继等操作。通过一系列旋转和调整,实现了高效的数据操作。博客中给出了具体的算法实现和代码示例,适用于解决需要快速查询和更新的数据问题。
摘要由CSDN通过智能技术生成

传送门

分析

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  • 插入 x x x
  • 删除 x x x 数(若有多个相同的数,因只删除一个)
  • 查询 x x x 数的排名(排名定义为比当前数小的数的个数 + 1 +1 +1 )
  • 查询排名为 x x x 的数
  • x x x 的前驱(前驱定义为小于 x x x,且最大的数)
  • x x x 的后继(后继定义为大于 x x x,且最小的数)

行!就用Splay了,只要理解,rotate和splay,直接往上套,别管什么常数了

  • 插入,找到 x x x的前驱和后继,先旋前驱到根,再旋后继到根右子树,在后继的左子树上直接加,判断是否存在,如果存在的话,直接加个数即可
  • 删除,同样操作,找到 x x x 的前驱和后继,同样的旋转,在后继的左子树上删除值,判断个数来删除节点
  • 查询排名,二分值找到节点, 旋转到根,输出左子树的 s z sz sz 即可(因为中序遍历有序)
  • 查第几大,在 S p l a y Splay Splay 上通过 s z sz sz 二分查找即可
  • 找前驱,二分找到节点 x x x,往左子树的右子树找值
  • 找后继,二分找到节点 x x x,往右子树的左子树找值

此时注意
鉴于找值可能会找不到值,所以只找到自己,或者前驱后继为止(不会到空)
前驱后继那里特判是否直接就找到了前驱或者后继,之际返回即可

代码

//P3369
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;

struct Tr {
	int k, fa, sz, cnt;
	int son[2];
	int& l = son[0];
	int& r = son[1];
}tr[MAX_N];
int indx = 0;
int root = 0;

void push_up(int rt) {
	tr[rt].sz = tr[tr[rt].l].sz + tr[tr[rt].r].sz + tr[rt].cnt;
}

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

void rotate(int x) {
	int p = tr[x].fa;
	int q = tr[p].fa;
	int side = which(x);
	
	tr[tr[p].son[side] = tr[x].son[side^1]].fa = p;
	tr[tr[x].son[side^1] = p].fa = x;
	tr[x].fa = q;
	
	if (q) {	
		tr[q].son[tr[q].son[1] == p] = x;
	}
	
	push_up(p);
	push_up(x);
}

void splay(int x, int tar) {
	for (int p; (p = tr[x].fa) != tar; rotate(x)) {
		if (tr[p].fa != tar) rotate(which(x) == which(p) ? p : x);
	}
	if (!tar) root = x;
} 

void insert(int x) {
	int rt = root, p = 0;
	while (rt && tr[rt].k != x) {
		p = rt;
		rt = tr[rt].son[x > tr[rt].k];
	}
	if (rt) {
		tr[rt].cnt++;
		splay(rt, 0);
		return;
	}
	rt = ++indx;
	if (p) {
		tr[p].son[x > tr[p].k] = rt;
	}
	tr[rt].fa = p;
	tr[rt].k = x;
	tr[rt].sz = tr[rt].cnt = 1;
	splay(rt, 0);
}

int find(int x) {
	int rt = root;
	while (tr[rt].son[x > tr[rt].k] && tr[rt].k != x) {
		rt = tr[rt].son[x > tr[rt].k];
	}
	splay(rt, 0);
	return rt;
}

int find_pre(int x) {
	int rt = find(x);
	if (tr[rt].k < x) return rt;
	rt = tr[rt].l;
	while (tr[rt].r) {
		rt = tr[rt].r;
	}
	return rt;
}

int find_nxt(int x) {
	int rt = find(x);
	if (tr[rt].k > x) return rt;
	rt = tr[rt].r;
	while (tr[rt].l) {
		rt = tr[rt].l;
	}
	return rt;
}

void del(int x) {
	int pre = find_pre(x);
	int nxt = find_nxt(x);
	splay(pre, 0);
	splay(nxt, pre);
	int rt = tr[nxt].l;
	if (tr[rt].cnt > 1) {
		--tr[rt].cnt;
		splay(rt, 0);
	} else {
		tr[nxt].l = 0;
	}
}

int query_rk(int x) {
	int rt = find(x);
	return tr[tr[rt].l].sz;
}

int fetch(int x) {
	int rt = root;
	while (x) {
		if (tr[tr[rt].l].sz >= x) {
			rt = tr[rt].l;
			continue;
		}
		x -= tr[tr[rt].l].sz;
		if (x <= tr[rt].cnt) return rt;
		x -= tr[rt].cnt;
		rt = tr[rt].r;
	}
}

void solve(){
	sc("%d", &N);
	insert(-1e9);
	insert(1e9);
	
	int opt, x;
	for (int i = 1; i <= N; ++i) {
		sc("%d%d", &opt, &x);
		if (opt == 1) {
			insert(x);
		} else if (opt == 2) {
			del(x);
		} else if (opt == 3) {
			pr("%d\n", query_rk(x), i);
		} else if (opt == 4) {
			pr("%d\n", tr[fetch(x+1)].k, i);
		} else if (opt == 5) {
			pr("%d\n", tr[find_pre(x)].k, i);
		} else {
			pr("%d\n", tr[find_nxt(x)].k, i);
		}
	}
}

signed main()
{
	#ifndef ONLINE_JUDGE
	//FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();

	return AC;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hexrt

客官,请不要给我小费!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值