首页 > 其他分享 >FHQ-treap学习笔记

FHQ-treap学习笔记

时间:2024-02-29 10:34:48浏览次数:26  
标签:int tr pos 笔记 treap key FHQ

平衡树,即平衡二叉搜索树。
二叉搜索树(BST),它或者是一棵空树,或者是具有下列性质的二叉树:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉排序树。(from 百度百科)
而在使用这种数据结果时,如果题目数据是顺序的,那么这棵BST将被卡成一条链,复杂度就假了。
于是就出现了平衡二叉搜索树,这种树可以在维持BST的性质的同时,通过各种手段,保证树高在log级。
今天,我将介绍的是FHQ-treap
FHQ-treap每个点除了维护原本的key值,还维护一个在节点新加入时随机生成的val值
要求要保证key值满足BST的性质,val值满足小根堆(大根堆)的性质。
FHQ-treap由两个主要操作,split和merge
如下:

1.split

split可以将一颗FHQ-treap分裂为两棵FHQ-trea,我们成为左树和右树,其中左树的key值<=x,右树的key值>x(x为split传入的参数)

void split(int pos,int x,int &l,int &r){//pos表示当前便利到的节点,l,r表示当前左树、右树的根。
	if(!pos){//如果当前遍历到的节点为0,那么为空子树,左右子树根都是0
		l=r=0;
		return ;
	}
	if(tr[pos].key<=x){//如果当前节点key值<=x,那么它及它的左子树都应当被分为左树
		l=pos;
		split(tr[l].rs,x,tr[l].rs,r);
	}else{//反之,同上
		r=pos;
		split(tr[r].ls,x,l,tr[r].ls);
	}
	push_up(pos);//由于树的结构改变,记得push_up
}

2.merge

merge可以将两棵值域无交集的FHQ-treap合并

int merge(int l,int r){//两颗treap的根,返回合并后的根
	if(l==0 || r==0)return l+r;//如果其中由一颗空子树,那么返回另一个;如果都是就返回0
	if(tr[l].val<=tr[r].val){//如果l的val<=r的val,那么将r并入l(为了满足小根堆的性质)
		tr[l].rs=merge(tr[l].rs,r);
		push_up(l);//同理,不要忘记push_up
		return l;
	}else{//同上
		tr[r].ls=merge(l,tr[r].ls);
		push_up(r);
		return r;
	}
}

完成了split和merge操作,其他各种平衡树上得操作就很简单了。
便可以完成普通平衡树

#include<bits/stdc++.h>
using namespace std;
mt19937 rnd(time(0));
int n;
struct tree{
	int sz,val,key,ls,rs;
}tr[200005];
int rt=0,tot=0;
int get(int x){
	tr[++tot].sz=1;
	tr[tot].val=rnd();
	tr[tot].key=x;
	return tot;
}
void push_up(int pos){
	tr[pos].sz=tr[tr[pos].ls].sz+tr[tr[pos].rs].sz+1;
}
void split(int pos,int x,int &l,int &r){
//	printf("%d %d %d %d\n",pos,x,l,r);
	if(!pos){
		l=r=0;
		return ;
	}
	if(tr[pos].key<=x){
		l=pos;
		split(tr[l].rs,x,tr[l].rs,r);
	}else{
		r=pos;
		split(tr[r].ls,x,l,tr[r].ls);
	}
	push_up(pos);
}
int merge(int l,int r){
	if(l==0 || r==0)return l+r;
	if(tr[l].val<=tr[r].val){
		tr[l].rs=merge(tr[l].rs,r);
		push_up(l);
		return l;
	}else{
		tr[r].ls=merge(l,tr[r].ls);
		push_up(r);
		return r;
	}
}
void insert(int x){
	int p=0,q=0;
	split(rt,x-1,p,q);
	rt=merge(merge(p,get(x)),q);
}
void del(int x){
	int p=0,q=0,r=0;
	split(rt,x-1,p,q);split(q,x,q,r);
	q=merge(tr[q].ls,tr[q].rs);
	rt=merge(merge(p,q),r);
}
int get_rk(int x){
	int p=0,q=0,res=0;
	split(rt,x-1,p,q);
	res=tr[p].sz+1;
	rt=merge(p,q);
	return res;
}
int get_num(int pos,int x){
	int u=tr[tr[pos].ls].sz;
	if(x==u+1)return tr[pos].key;
	else if(x<=u)return get_num(tr[pos].ls,x);
	else return get_num(tr[pos].rs,x-u-1);
}
int pre(int x){
	int p=0,q=0,res=0;
	split(rt,x-1,p,q);
//	printf("%d %d\n",p,tr[p].sz);
	res=get_num(p,tr[p].sz);
	rt=merge(p,q);
	return res;
}
int suf(int x){
	int p=0,q=0,res=0;
	split(rt,x,p,q);
	res=get_num(q,1);
	rt=merge(p,q);
	return res;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int op,x;scanf("%d%d",&op,&x);
		if(op==1)insert(x);
		else if(op==2)del(x);
		else if(op==3)printf("%d\n",get_rk(x));
		else if(op==4)printf("%d\n",get_num(rt,x));
		else if(op==5)printf("%d\n",pre(x));
		else printf("%d\n",suf(x));
	}
	return 0;
}

请注意区分val和key的区别

标签:int,tr,pos,笔记,treap,key,FHQ
From: https://www.cnblogs.com/kentsbk/p/18042872

相关文章

  • 搜索 学习笔记
    目录最基础的搜索1.1bfs1.2dfs双向搜索2.1双向广搜2.2双向宽搜2.3meetinthemiddle3搜索的优化3.1记忆化搜索3.2最优性剪枝3.3可行性剪枝3.4其他方法迭代加深搜索DancingLinks\(\alpha-\beta\)剪枝A/IDA正文1.最基础的搜索......
  • 分块 学习笔记
    目录分块思想分块基础操作2.1\(O(\sqrtn)-O(\sqrtn)\)区间加、区间查询2.2\(O(1)-O(\sqrtn)\)区间加、单点查询2.3\(O(\sqrtn)-O(1)\)单点加、区间查询各种分块思路3.1对序列分块普通区间和维护区间\(k\)大等3.2对值域分块3.3对操作分块3.......
  • 简单字符串 学习笔记
    目录字符串哈希字典树字典树的倒序建树KMP正文1.字符串哈希1.1基础可以在数字和字符串之间快速转化。考虑一个哈希函数:\(f(s)=(\sum\limits_{i=0}^{n}s_i\timesbase^{n-i+1})\)。容易发现这些值是唯一的。但是取模时需要选一个较大模数,减少冲突概率。cons......
  • RASP部署笔记
    一.什么是RASPRASP全称是RuntimeApplicationSelfProtect,其基本思路是将防护代码注入到应用运行的关键函数中,实现应用运行态的入侵检测与防护。例如,为了检测任意文件上传攻击,我们可以将防护代码注入到文件写入基础函数中。在java中,这个函数是FileOutputStream的构造函数。我们通......
  • 《系统科学方法概论》第三章读书笔记
    读完《系统科学方法概论》的第三章后,我对系统科学方法有了更深入的理解和认识。这一章介绍了系统分析方法,让我明白了如何从整体的角度去理解和研究复杂的系统。系统分析强调对系统的各个组成部分进行全面的考察,并考虑它们之间的相互关系。这种方法帮助我更好地把握系统的本质和特......
  • 《系统科学方法概论》第四章读书笔记
    读完《系统科学方法概论》的第四章后,我对系统分析方法有了更深入的理解和认识。这一章详细介绍了系统分析的概念、步骤和方法,让我明白了系统分析在解决复杂问题中的重要性。通过对系统的各个组成部分进行分解和分析,我们可以更好地理解系统的整体行为和特性。我认识到系统分析需......
  • 《系统科学方法概论》第五章读书笔记
    读完《系统科学方法概论》的第五章后,我对系统分析方法有了更深入的理解和认识。这一章详细介绍了系统分析的概念、步骤和方法,让我明白了系统分析在解决复杂问题中的重要性。通过对系统的各个组成部分进行分解和研究,我们可以更好地理解系统的整体行为和特性。我认识到系统分析需......
  • 《程序是怎样跑起来的》第九章读书笔记
    在阅读第九章后,我对文件和文件系统有了更全面的认识。这一章详细介绍了文件的概念、文件的存储与管理方式,以及文件系统的工作原理。我了解到文件是数据的长期存储形式,它们在计算机系统中起着至关重要的作用。文件系统提供了一种组织和管理文件的方式,使得我们能够方便地存储、读取......
  • 《程序是怎样跑起来的》第十章读书笔记
    读完第十章后,我对文件和输入输出有了更全面的认识。这一章详细介绍了文件的概念、文件的操作以及输入输出的处理方式。我了解到文件作为数据的持久存储介质,在程序中起着重要的作用。通过文件,我们可以将数据长期保存下来,并在需要时进行读取和处理。文件的管理和操作需要注意文件的......
  • C++ 多线程笔记1 线程的创建
    C++多线程笔记1线程的创建里面代码会用到的头文件#include<iostream>#include<string>#include<memory>#include<thread>#include<vector>#include<stdlib.h>#include<cmath>#include<chrono>#include<ctime>入门例子vo......