洛谷 P3369 【模板】普通平衡树

原题地址:https://www.luogu.org/problemnew/show/P3369

题目描述

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

  1. 插入xx数
  2. 删除xx数(若有多个相同的数,因只删除一个)
  3. 查询xx数的排名(排名定义为比当前数小的数的个数+1+1。若有多个相同的数,因输出最小的排名)
  4. 查询排名为xx的数
  5. 求xx的前驱(前驱定义为小于xx,且最大的数)
  6. 求xx的后继(后继定义为大于xx,且最小的数)

输入输出格式

输入格式:

 

第一行为nn,表示操作的个数,下面nn行每行有两个数optopt和xx,optopt表示操作的序号( 1 \leq opt \leq 61≤opt≤6 )

 

输出格式:

 

对于操作3,4,5,63,4,5,6每行输出一个数,表示对应答案

 

输入输出样例

输入样例#1: 

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

输出样例#1: 

106465
84185
492737

说明

时空限制:1000ms,128M

1.n的数据范围: n \leq 100000n≤100000

2.每个数的数据范围: [-{10}^7, {10}^7][−107,107]

来源:Tyvj1728 原名:普通平衡树

在此鸣谢

splay代码如下

#include <iostream>
#include <cstdio>
#define maxn 100005
#define INF 100000000
using namespace std;
int root;
int point;
int p; //当前还有几个节点 
struct splay{
	int father;  //父节点 
	int child[2]; //左孩子,右孩子 
	int recy, sum; //data出现次数,左右孩子的数据总数+recy 
	int data;   //数据 
}tree[maxn];
void connect(int father, int son, int i)  //使son成为father的孩子 
{
	tree[son].father = father;
	tree[father].child[i] = son;
} 
bool get(int x)  //是其根节点的左孩子还是右孩子 
{
	int father = tree[x].father;
	return tree[father].child[1] == x;
}
void update(int x) //更新sum 
{
	tree[x].sum = tree[tree[x].child[0]].sum + tree[tree[x].child[1]].sum + tree[x].recy;
}
void rotate(int x) //旋转 
{
	int y = tree[x].father;
	int z = tree[y].father;
	int u = get(x);
	int v = get(y);
	connect(y, tree[x].child[u^1], u);
	connect(z, x, v);
	connect(x, y, u^1);
	update(y);
	update(x);
}
void splay(int now, int to) //把now旋转到to位置 
{
	if(to == root)  //
		root = now;
	to = tree[to].father;
	while(tree[now].father != to){
		int up = tree[now].father;
		if(tree[up].father == to)
			rotate(now);
		else if(get(now) == get(up)){
			rotate(up);
			rotate(now);
		}
		else {
			rotate(now);
			rotate(now);
		}
	}
}
int crepoint(int v, int fa)
{
	point ++;
	tree[point].data = v;
	tree[point].child[0] = tree[point].child[1] = 0;
	tree[point].father = fa;
	tree[point].sum = tree[point].recy = 1;
	return point;
}
int insert(int v)
{
	if(p == 0){  //如果没有节点了 
		root = crepoint(v, 0);
		return root;
	}
	int now = root;
	while(true){
		tree[now].sum ++; //进过的每一个点都sum++ 
		if(tree[now].data == v){
			tree[now].recy ++;	
			return now;
		}
		else {
			int t = tree[now].data < v;
			if(tree[now].child[t] == 0){
				int son = crepoint(v, now);
				tree[now].child[t] = son;
				return son;
			}
			now = tree[now].child[t];
		}
	}
	return 0;
} 
void push(int v)   
{
	int t = insert(v);
	splay(t, root);
	p ++;
}
void del(int x)
{
	tree[x].child[0] = tree[x].child[1] = tree[x].data = tree[x].father = tree[x].recy = tree[x].sum = 0;
}
void pop(int v)
{
	int now = root;
	while(now != 0){
		if(v == tree[now].data){
			splay(now, root);  //把要删除的节点旋转到根 
			if(tree[now].recy > 1){
				tree[now].recy --;
				tree[now].sum --;
			}	
			else {	
				if(!tree[now].child[0]){  //如果没有左子树,直接删掉此节点,然后把右孩子设为根节点 
					root = tree[now].child[1];
					tree[root].father = 0;
				}
				else {
					int left = tree[now].child[0];
					while(tree[left].child[1])    //找到左子树的最大值节点 
						left = tree[left].child[1];
					splay(left, tree[now].child[0]);  //把左子树最大节点旋转到根节点左孩子的位置 
					connect(left, tree[now].child[1], 1);  //让左子树最大节点作为新的根 
					update(left);   //连接后更新 
					root = left;
					tree[root].father = 0;
				}
				del(now); //删除节点 
			}
			p --;
			return;
		}
		else {
			int t = tree[now].data < v;
			now = tree[now].child[t];
		}
	}
}
int rnk(int v)  //返回v是第几大 
{
	int now = root;
	int r = 0;
	while(true){
		int left = tree[now].child[0];
		if(tree[now].data == v){
			r = r + tree[left].sum + 1;
			splay(now, root);   //旋转后left。sum的值会变 
			return r;
		}
		else if(tree[now].data < v){
			r += tree[left].sum + tree[now].recy;
			now = tree[now].child[1];
		}
		else 
			now = tree[now].child[0];
	}
}
int atrank(int n)  //返回第n大的数 
{
	int now = root;
	while(true){
		int left = tree[now].child[0];
		if(tree[left].sum >= n){
			now = left;
		}
		else if(tree[left].sum + tree[now].recy < n){
			n -= tree[left].sum + tree[now].recy;
			now = tree[now].child[1];
		}
		else {
			splay(now, root);  //旋转到根 
			return tree[now].data;
		}			
	}
}
int lower(int v) //前驱 
{
	int ans = -INF; 
	int now = root;
	while(now){
		//cout << "!! " << now << " " << tree[now].data << endl;//
		if(tree[now].data < v && tree[now].data > ans)
			ans = tree[now].data;
		if(tree[now].data >= v)
			now = tree[now].child[0];
		else
			now = tree[now].child[1];
	}
	return ans;
}
int upper(int v) //后继 
{
	int ans = INF;
	int now = root;
	while(now){
		if(tree[now].data > v && tree[now].data < ans)
			ans = tree[now].data;
		if(tree[now].data <= v)
			now = tree[now].child[1];
		else
			now = tree[now].child[0];
	}
	return ans;
}
int main()
{
	int n;
	cin >> n;
	for(int i = 0; i < n; i ++){
		int opt, x;
		scanf("%d%d", &opt, &x);
		//cin >> opt >> x;
		if(opt == 1){
			push(x);
		}
		else if(opt == 2){
			pop(x);
		}
		else if(opt == 3){
			printf("%d\n", rnk(x));
			//cout << rnk(x) << endl;
		}
		else if(opt == 4){
			printf("%d\n", atrank(x));
		}
		else if(opt == 5){
			printf("%d\n", lower(x));
		}
		else {
			printf("%d\n", upper(x));
		}
	}
	return 0;
}

7.12更新

常数更小更容易写的LeafyTree代码如下:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ratio 4
using namespace std;
typedef long long ll;

inline int read(){
    int res = 0, w = 0; char ch = 0;
    while(!isdigit(ch)){
        w |= ch == '-', ch = getchar();
    }
    while(isdigit(ch)){
        res = (res << 3) + (res << 1) + (ch ^ 48);
        ch = getchar();
    }
    return w ? -res : res;
}

const int N = 100005;

int siz[N << 2], data[N << 2], lch[N << 2], rch[N << 2]; 
int cnt, root;

void merge(int l, int r)
{
    ++cnt;
    siz[cnt] = siz[l] + siz[r];
    data[cnt] = data[r];        
    lch[cnt] = l;
    rch[cnt] = r;
}

void rotate(int k, bool flg)
{
    if(flg){
        merge(rch[lch[k]], rch[k]);
        lch[k] = lch[lch[k]];
        rch[k] = cnt;
    }
    else {
        merge(lch[k], lch[rch[k]]);
        lch[k] = cnt;
        rch[k] = rch[rch[k]];
    }
}

void maintain(int k)
{
    if(siz[lch[k]] > siz[rch[k]] * ratio)
        rotate(k, 1);
    else if(siz[rch[k]] > siz[lch[k]] * ratio)
        rotate(k, 0);
}

int newnode(int v)
{
    ++cnt;
    siz[cnt] = 1;
    data[cnt] = v;
    return cnt;
}

void pushup(int k)
{
    if(siz[lch[k]] == 0)
        return;
    siz[k] = siz[lch[k]] + siz[rch[k]];
    data[k] = data[rch[k]];
}

void insert(int k, int x)
{
    if(siz[k] == 1){
        lch[k] = newnode(min(data[k], x));
        rch[k] = newnode(max(data[k], x));
        pushup(k);
        return;
    }
    insert(x <= data[lch[k]] ? lch[k] : rch[k], x);
    maintain(k);
    pushup(k);
}

void cpynode(int a, int b)
{
    siz[a] = siz[b];
    data[a] = data[b];
    lch[a] = lch[b];
    rch[a] = rch[b];
}

void del(int k, int fa, int x)
{
    if(siz[k] == 1){
        cpynode(fa, k == lch[fa] ? rch[fa] : lch[fa]);
        return;
    }
    del(x <= data[lch[k]] ? lch[k] : rch[k], k, x);
    maintain(k);
    pushup(k);
}

int rnk(int k, int x)
{
    if(siz[k] == 1)
        return 1;
    if(x <= data[lch[k]])
        return rnk(lch[k], x);
    else 
        return rnk(rch[k], x) + siz[lch[k]];
}

int atrank(int k, int x)
{
    if(siz[k] == x)
        return data[k];
    if(x > siz[lch[k]])
        return atrank(rch[k], x - siz[lch[k]]);
    else
        return atrank(lch[k], x);
}

int lower(int x)
{
    return atrank(root, rnk(root, x) - 1);
}

int upper(int x)
{
    return atrank(root, rnk(root, x + 1));
}

int main()
{
    int n;
    scanf("%d", &n);
    root = newnode(INF);
    for(int i = 1; i <= n; i ++){
        int opt, x;
        scanf("%d%d", &opt, &x);
        if(opt == 1)
            insert(root, x);
        else if(opt == 2)
            del(root, 0, x);
        else if(opt == 3)
            printf("%d\n", rnk(root, x));
        else if(opt == 4)
            printf("%d\n", atrank(root, x));
        else if(opt == 5)
            printf("%d\n", lower(x));
        else if(opt == 6)
            printf("%d\n", upper(x));
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值