【模板】Treap(详细注释)+BZOJ3224

有关Treap,很多博客都给出了详细的讲解,这里就只给出Treap的模板代码和详细的注释,希望大家对Treap有更详细的理解。

模板如下(同时也是BZOJ3224的代码)。

注意在BZOJ和POJ等OJ上提交的时候不要使用srand()函数,否则会RE。

#include<cstdio>
#include<ctime>
#include<algorithm>
using namespace std;
const int maxn=100005;
int n;
struct Node{
	int LeftChild,RightChild,v,size,w,rnd;
	//w 表示 相同元素个数,v 表示 权值,size 表示 子树大小,rnd 表示随机权值
};
struct Treap{
	int root,ans,size;
	Node Tree[maxn];
	void Update(int k){//更新结点信息
		Tree[k].size=Tree[Tree[k].LeftChild].size+Tree[Tree[k].RightChild].size+Tree[k].w;
	}
	void RTurn(int &k){//旋转操作,不明白画个图理解一下吧.
		int t=Tree[k].LeftChild;Tree[k].LeftChild=Tree[t].RightChild;Tree[t].RightChild=k;
		Tree[t].size=Tree[k].size;Update(k),k=t;
	}
	void LTurn(int &k){//旋转操作,不明白画个图理解一下吧.
		int t=Tree[k].RightChild;Tree[k].RightChild=Tree[t].LeftChild;Tree[t].LeftChild=k;
		Tree[t].size=Tree[k].size;Update(k),k=t;
	}
	void insert(int &k,int x){//插入操作.
		if(k==0){//k==0需新建节点.
			size++;k=size;//新建节点.
			Tree[k].size=Tree[k].w=1;Tree[k].v=x;Tree[k].rnd=rand();//新节点初始化.
			return;
		}
		Tree[k].size++;//插入k所在的子树中,size++.
		if (Tree[k].v==x) Tree[k].w++;
		//找到相同键值的节点,键值出现次数+1.
		else if (x>Tree[k].v){//仍未找到插入位置,根据性质向左右子查找.
			insert(Tree[k].RightChild,x);//插入右子.
			if (Tree[Tree[k].RightChild].rnd<Tree[k].rnd) LTurn(k);
			//根据随机优先级旋转树来保持堆性质.
			//此处维护小根堆,大根堆亦可,将小于号改为大于号即可.
		}
		else{
			insert(Tree[k].LeftChild,x);//插入左子.
			if (Tree[Tree[k].LeftChild].rnd<Tree[k].rnd) RTurn(k);
			//维护堆性质.
		}
	}
	void erase(int &k,int x){//删除操作.
		if (k==0) return ;//未找到待删除节点,返回.
		if (Tree[k].v==x){//找到待删除节点.
			if (Tree[k].w>1){//若键值出现过多于一次.
				Tree[k].w--,Tree[k].size--;//次数-1,子树大小-1.
				return ;
			}
			if (Tree[k].LeftChild*Tree[k].RightChild==0) k=Tree[k].LeftChild+Tree[k].RightChild;
			//如果没有儿子或只有一个儿子,那么直接删除.
			else if (Tree[Tree[k].LeftChild].rnd<Tree[Tree[k].RightChild].rnd) RTurn(k),erase(k,x);
			else LTurn(k),erase(k,x);
			//如果有两个儿子,就在两个儿子中选择随机优先级小的那个节点替换当前节点(通过旋转操作).
			//再递归删除.
		}
		else if (x>Tree[k].v) Tree[k].size--,erase(Tree[k].RightChild,x);
		else Tree[k].size--,erase(Tree[k].LeftChild,x);
		//还没有找到待删除节点,继续根据性质递归查找.
		//因为已经确定待删除节点再子树k中,就可以顺便将k的子树大小-1.
	}
	int kth(int k,int x){//查询第k大.
		//此处k为节点编号,x为要查询的值.
		if (k==0) return 0;//递归到空节点,直接返回0.
		if (x<=Tree[Tree[k].LeftChild].size) return kth(Tree[k].LeftChild,x);
		//k小于左子的子树大小,说明第k大在左子树中,查询左子树中的第k大.
		else if (x>Tree[Tree[k].LeftChild].size+Tree[k].w) return kth(Tree[k].RightChild,x-Tree[Tree[k].LeftChild].size-Tree[k].w);
		//k大于左子的子树大小加本节点键值出现次数,说明第k大在右子中.
		//查询右子树中的第k-Tree[Tree[k].LeftChild].size+Tree[k].w(减去左子树的大小和本节点键值出现次数)大.
		else return Tree[k].v;
		//此时的x>Tree[Tree[k].LeftChild].size且x<=Tree[Tree[k].LeftChild].size+Tree[k].w
		//说明当前的节点就是第k大,返回即可.
	}
	int rank(int k,int x){//查询x的排名(x不一定在Treap中).
		if (k==0) return 0;//递归到空节点,直接返回0.
		if (Tree[k].v==x) return Tree[Tree[k].LeftChild].size+1;
		//x存在于Treap中,且已找到,返回左子树的大小+1即为排名.
		else if (x>Tree[k].v) return Tree[Tree[k].LeftChild].size+Tree[k].w+rank(Tree[k].RightChild,x);
		//根据性质递归查找.
		//在右子中查找,应返回其在右子中的排名+左子的子树大小和本节点键值出现次数.
		else return rank(Tree[k].LeftChild,x);
		//在左子中查找,返回其在左子中的排名即可.
	}
	void predecessor(int k,int x){//查询前驱.
		if(k==0) return;//递归到空节点,返回.
		if(Tree[k].v<x) this->ans=k,predecessor(Tree[k].RightChild,x);
		//本节点的键值小于查询值,更新答案,向更大的方向查找(右子).
		else predecessor(Tree[k].LeftChild,x);
		//本节点的键值大于等于查询值,向更小的方向查找(左子).
	}
	void successor(int k,int x){//查询后继.
		if (k==0) return;//递归到空节点,返回. 
		if (Tree[k].v>x) this->ans=k,successor(Tree[k].LeftChild,x);
		//本节点键值大于查询值,更新答案,向更小的方向查找(左子).
		else successor(Tree[k].RightChild,x);
		//本节点的键值大于等于查询值,向更大的方向查找(右子).
	}
	void Insert(int x){insert(root,x);}
	void Erase(int x){erase(root,x);}
	int Kth(int k){return kth(root,k);}
	int Rank(int x){return rank(root,x);}
	int Predecessor(int x){predecessor(root,x);return Tree[ans].v;}
	int Successor(int x){successor(root,x);return Tree[ans].v;}
}Treap;
int main(){
//	freopen("BZOJ3224.in","r",stdin);
//	freopen("BZOJ3224.out","w",stdout);
//	srand(time(NULL));
	scanf("%d",&n);
	for (int i=1,opt,x;i<=n;i++){
		scanf("%d%d",&opt,&x);
		if (opt==1) Treap.Insert(x);
		if (opt==2) Treap.Erase(x);
		if (opt==3) printf("%d\n",Treap.Rank(x));
		if (opt==4) printf("%d\n",Treap.Kth(x));
		if (opt==5) printf("%d\n",Treap.Predecessor(x));
		if (opt==6) printf("%d\n",Treap.Successor(x));
	}
	return 0;
}

 

课程设计报告:总体方案设计说明 一、软件开发环境配置 本系统采用C++作为核心编程语言,结合Qt 5.12.7框架进行图形用户界面开发。数据库管理系统选用MySQL,用于存储用户数据与小精灵信息。集成开发环境为Qt Creator,操作系统平台为Windows 10。 二、窗口界面架构设计 系统界面由多个功能模块构成,各模块职责明确,具体如下: 1. 起始界面模块(Widget) 作为应用程序的入口界面,提供初始导航功能。 2. 身份验证模块(Login) 负责处理用户登录与账户注册流程,实现身份认证机制。 3. 游戏主大厅模块(Lobby) 作为用户登录后的核心交互区域,集成各项功能入口。 4. 资源管理模块(BagWidget) 展示用户持有的全部小精灵资产,提供可视化资源管理界面。 5. 精灵详情模块(SpiritInfo) 呈现选定小精灵的完整属性数据与状态信息。 6. 用户名录模块(UserList) 系统内所有注册用户的基本信息列表展示界面。 7. 个人资料模块(UserInfo) 显示当前用户的详细账户资料与历史数据统计。 8. 服务器精灵选择模块(Choose) 对战准备阶段,从服务器可用精灵池中选取参战单位的专用界面。 9. 玩家精灵选择模块(Choose2) 对战准备阶段,从玩家自有精灵库中筛选参战单位的操作界面。 10. 对战演算模块(FightWidget) 实时模拟精灵对战过程,动态呈现战斗动画与状态变化。 11. 对战结算模块(ResultWidget) 对战结束后,系统生成并展示战斗结果报告与数据统计。 各模块通过统一的事件驱动机制实现数据通信与状态同步,确保系统功能的连贯性与数据一致性。界面布局遵循模块化设计原则,采用响应式视觉方案适配不同显示环境。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值