HYSBZ - 3224 Tyvj 1728 普通平衡树 (splay树板子题)

本文深入探讨了Splay树的实现与应用,通过详细解释动态插入、查询排名、查找特定排名元素等操作,提供了完整的Splay树代码实现。文章还涵盖了如何进行旋转、平衡调整及节点删除等关键操作。

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

HYSBZ - 3224 Tyvj 1728 普通平衡树 (splay树板子题)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3224

解题思路:

动态的插入数
询问x为插入的数中第几个
询问第x数是啥
询问<x的最大的一个
询问>x的最小的一个
splay树板子题。
权当模板存下。
代码较长,抄板子的时候一定得仔细,抄错一点地方都不对

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000007;
int f[maxn];//i的父亲节点
int ch[maxn][2];//ch[i][0]表示i的左儿子,ch[i][j]表示i的右儿子
int key[maxn];//节点i代表的数字
int cnt[maxn];//cnt[i]表示i节点的关键字出现的次数,相当于权值
int size[maxn];//size[i]表示包括i的这个子树的大小。
int sz = 0;//整棵树的大小
int rt = 0;//整棵树的根

//将当前各项值都清0 (用于删除之后)
void clear(int x){
	f[x] = cnt[x] = ch[x][0] = ch[x][1] =size[x] = key[x] =0;
}

//判断这个节点是左孩子还是右孩子 1是右孩子 0是左孩子
bool get(int x){
	return ch[f[x]][1] == x;
}

//更新当前节点的size值(用于修改之后)
void pushup(int x){
	//更新这个节点的权值
	if(x){
		size[x] = cnt[x];
		if(ch[x][0]) size[x] += size[ch[x][0]];
		if(ch[x][1]) size[x] += size[ch[x][1]];
	}
}

void rotate(int x)
{
    int old=f[x],oldf=f[old],which=get(x);
    ch[old][which]=ch[x][which^1]; f[ch[old][which]]=old;  //这两句的意思是:
    //我的儿子过继给我的爸爸;同时处理父子两个方向上的信息
    ch[x][which^1]=old; f[old]=x;
    //我给我爸爸当爹,我爸爸管我叫爸爸
    f[x]=oldf;//我的爷爷成了我的爸爸
    if (oldf) ch[oldf][ch[oldf][1]==old]=x;
    pushup(old); pushup(x);//分别维护信息 
}


//splay树平衡 把x旋转为根节点
void splay(int x){
	for(int fa;fa=f[x];rotate(x)){
		if(f[fa]){
			rotate((get(x)==get(fa))?fa:x);
		}
	}
	rt = x;
}

//x为插入点的权值
void insert(int x){
	if(rt == 0){///空树
		sz++;
		key[sz] = x;
		rt = sz;
		cnt[sz] = size[sz] =1;
		f[sz] = ch[sz][0] =ch[sz][1] = 0;
		return;
	}

	int now = rt,fa = 0;
	while(1){
		if(x == key[now]){//这个值已经出现过了
			cnt[now]++;
			pushup(now);
			pushup(fa);
			splay(now);
			return;
		}
		fa = now;
		now = ch[now][key[now]<x];//往左还是往右走
		if(now == 0){
			sz++;
			//sz 为当前这个节点点的编号
			size[sz] = cnt[sz] = 1;
			ch[sz][0] = ch[sz][1] = 0;
			ch[fa][x>key[fa]] = sz;///根据加入的点的顺序标号
			f[sz] = fa;
			key[sz] = x;
			pushup(fa);
			splay(sz);
			return ;
		}
	}
}


//查询x的排名
int rnk(int x){
	int now = rt;
	int ans = 0;
	while(1){
		if(x<key[now]) now = ch[now][0];//左子树 
		else{ 
			ans += size[ch[now][0]];//加上左子树的贡献 
			if(x == key[now]){
				splay(now);
				return ans + 1;
			}
			ans += cnt[now];//次数
			now = ch[now][1];//到右孩子
		}
	}
}

//查询排名为x的点
int kth(int x){
	int now = rt;
	while(1){

		//左孩子
		if(ch[now][0]&&x<=size[ch[now][0]]){
			now = ch[now][0];
		}else{
			int tmp = size[ch[now][0]]+cnt[now];//左半部分
			if(x<=tmp) return key[now];
			x-=tmp;
			now = ch[now][1];
		}
	}
}

//前驱 找小于根节点的最大的那个 也就是找左子树中最大的数
int pre(){
	int now=ch[rt][0];
	while(ch[now][1]){
		now = ch[now][1];
	}
	return now;
}
//后继 找大于根节点的最小的那个 也就是找右子树中最小的那个
int next(){
	int now = ch[rt][1];
	while(ch[now][0]){
		now = ch[now][0];
	}
	return now;
}


//del 删除x这个数
void del(int x){
	rnk(x);//把x旋转到根节点的地方
	if(cnt[rt]>1){//有好几个x
		cnt[rt] --;
		pushup(rt);
		return ;
	}
	if(!ch[rt][0]&&!ch[rt][1]){//没孩子
		clear(rt);
		rt = 0;
		return;
	}
	if(!ch[rt][0]){
		int oldrt = rt;
		rt = ch[rt][1];
		f[rt] = 0;
		clear(oldrt);
		return;
	}else if(!ch[rt][1]){
		int oldrt = rt;
		rt = ch[rt][0];
		f[rt] = 0;
		clear(oldrt);
		return;
	}
	int oldrt = rt;
	int leftbig = pre();
	splay(leftbig);
	ch[rt][1]=ch[oldrt][1];
	f[ch[oldrt][1]] = rt;//此时的rt就是leftbig
	clear(oldrt);	
	pushup(rt);
}
int main(){
	int t;
	cin>>t;
	int op,x;
	while(t--){
		scanf("%d%d",&op,&x);
		if(op==1){
			insert(x);
		}else if(op==2){
			del(x);
		}else if(op==3){
			cout<<rnk(x)<<endl;
		}else if(op==4){
			cout<<kth(x)<<endl;
		}else if(op==5){
			 insert(x); printf("%d\n",key[pre()]); del(x);
		}else if(op==6){
			insert(x); printf("%d\n",key[next()]); del(x);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值