【模板】非旋转Treap

Treap,也叫做树堆,是指有一个随机附加域满足堆的性质的二叉搜索树。

如果一棵二叉搜索树插入节点的顺序是随机的,那我们得到的二叉搜索树在大多数情况下是平衡的,期望高度是log(n).

但有些情况下我们并不能得知所有待插入节点,打乱以后再插入,这时我们需要给二叉搜索树加上一个随机附加域,并使这个随机附加域(优先级)满足堆的性质,以此来实现“乱序插入”的想法,使二叉搜索树保持平衡。

Treap可以满足的序列操作:

1,插入一个数x

2,删除一个数x

3,查询x的排名

4,查询排名为x的数

5,查询x的前驱

6,查询x的后继

Treap的基本操作

1,newnode新建节点

2,split分裂 把Treap按照权值分割为两部分

3,merge合并 把Treap的两部分进行合并

4,insert插入 把Treap按照插入的权值Val分裂为两部分,然后merge左边和新点,再merge新的左边和右边

5,delete删除 把Treap分裂两次,中间那部分不管它,把两边merge一下

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<ctime>
 4 #define ls (a[u].l)
 5 #define rs (a[u].r)
 6 using namespace std;
 7 const int maxn=200010;
 8 int n,k,x,y,z,v,tot,root;
 9 struct treap{int l,r,v,rnd,size;}a[maxn];
10 void read(int &k){
11     k=0; int f=1; char c=getchar();
12     while(c<‘0‘||c>‘9‘)c==‘-‘&&(f=-1),c=getchar();
13     while(‘0‘<=c&&c<=‘9‘)k=k*10+c-‘0‘,c=getchar();
14     k*=f;
15 }
16 void newnode(int v){a[++tot]=(treap){0,0,v,rand()+1,1};}
17 void update(int u){a[u].size=a[ls].size+a[rs].size+1;}
18 void split(int u,int k,int &x,int &y){
19     if(!k){x=0; y=u; return;}
20     if(a[u].size==k){x=u; y=0; return;}
21     if(a[ls].size>=k)split(a[u].l,k,x,a[u].l),y=u;
22     else split(a[u].r,k-a[ls].size-1,a[u].r,y),x=u;
23     update(u);
24 }
25 int merge(int x,int y){
26     if(!(x*y)) return x+y;
27     if(a[x].rnd>a[y].rnd){
28         a[y].l=merge(x,a[y].l); update(y); return y;
29     }
30     else{
31         a[x].r=merge(a[x].r,y); update(x); return x;
32     }
33 }
34 int qrank(int u,int val){
35     if(!u) return 0;
36     if(a[u].v>=val) return qrank(ls,val);
37     return qrank(rs,val)+a[ls].size+1;
38 }
39 int qval(int u,int k){
40     if(a[ls].size+1==k) return a[u].v;
41     return a[ls].size>=k?qval(ls,k):qval(rs,k-a[ls].size-1);
42 }
43 int main(){
44     srand(time(0)); root=tot=1; a[root].v=2e9; a[root].size=1;
45     read(n);
46     while(n--){
47         read(k); read(v);
48         if(k==1){//插入
49             split(root,qrank(root,v),x,y);
50             newnode(v); root=merge(merge(x,tot),y);
51         }
52         if(k==2){//查询
53             split(root,qrank(root,v),x,y);
54             split(y,1,z,y); root=merge(x,y);
55         }
56         if(k==3) printf("%d\n",qrank(root,v)+1);//查询x的排名
57         if(k==4) printf("%d\n",qval(root,v));//查排名为x的数
58         if(k==5) printf("%d\n",qval(root,qrank(root,v)));//求x的前驱
59         if(k==6) printf("%d\n",qval(root,qrank(root,v+1)+1));//求x的后继
60     }
61     return 0;
62 }
63                     

洛谷3369

时间: 2024-08-29 18:50:18

【模板】非旋转Treap的相关文章

非旋转Treap

Treap是一种平衡二叉树,同时也是一个堆.它既具有二叉查找树的性质,也具有堆的性质.在对数据的查找.插入.删除.求第k大等操作上具有期望O(log2n)的复杂度.     Treap可以通过节点的旋转来实现其维持平衡的操作,详见旋转式Treap. 而旋转式Treap在对区间数据的操作上无能为力,这就需要非旋转式Treap来解决这些区间问题. 非旋转式Treap支持的操作 基本操作: 操作 说明 实现复杂度 Build 构造Treap O(n) Merge 合并Treap O(log2n) Sp

[bzoj3173]最长上升子序列_非旋转Treap

最长上升子序列 bzoj-3173 题目大意:有1-n,n个数,第i次操作是将i加入到原有序列中制定的位置,后查询当前序列中最长上升子序列长度. 注释:1<=n<=10,000,开始序列为空. 想法:显然,我们发现,我每次加入的数一定是当前序列中最大的,所以,刚刚加入的i,要么是当前序列中LIS的结尾,要么不属于LIS.根据这个性质,我们想到:在Treap中维护这样的性质,就是维护每个数加入节点的编号.然后,我们更新新节点的方式就是它的左子树和右子树的LIS取最大+1.其实最重要的就是如何加入

普通平衡树——非旋转treap

题目: 此为平衡树系列第一道:普通平衡树您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. 求x的前驱(前驱定义为小于x,且最大的数)6. 求x的后继(后继定义为大于x,且最小的数) n<=100000 所有数字均在-107到107内. 输入样例: 10 1 106465 4 1 1 317721 1 460929 1 644985 1 841

4923: [Lydsy1706月赛]K小值查询 平衡树 非旋转Treap

国际惯例的题面:这种维护排序序列,严格大于的进行操作的题都很套路......我们按照[0,k],(k,2k],(2k,inf)分类讨论一下就好.显然第一个区间的不会变化,第二个区间的会被平移进第一个区间,第三个区间的相对大小不会变化.于是我们直接把第二个区间拆了重构,一个一个插入第一个区间即可.因为每次这样做最少减半,所以每个元素只会被重构log次,复杂度nlog^2n.这种按照值域分离区间的操作,非旋转treap实现起来是最简单的......然而第一次写非旋转treap还是出了一点问题,注意它

平衡树讲解(旋转treap,非旋转treap,splay)

在刷了许多道平衡树的题之后,对平衡树有了较为深入的理解,在这里和大家分享一下,希望对大家学习平衡树能有帮助. 平衡树有好多种,比如treap,splay,红黑树,STL中的set.在这里只介绍几种常用的:treap和splay(其中treap包括旋转treap和非旋转treap). 一.treap treap这个词是由tree和heap组合而成,意思是树上的的堆(其实就是字面意思啦qwq).treap可以说是由二叉搜索树(BST)进化而来,二叉搜索树每个点满足它左子树中所有点权值都比它小,它右子

[bzoj1895][Pku3580]supermemo_非旋转Treap

supermemo bzoj-1895 Pku-3580 题目大意:给定一个n个数的序列,需支持:区间加,区间翻转,区间平移,单点插入,单点删除,查询区间最小值. 注释:$1\le n\le 6.1\cdot 10^6$. 想法: 这数据范围给的我真是醉了. 显然用平衡树,这里用非旋转Treap,题目让你干什么你就干什么. 区间加:撕出子树区间后打标记维护区间加. 区间翻转:撕出子树区间后打标记维护区间翻转. 区间平移:相当于两段相邻区间交换,所以撕成四部分:左边,第一个区间,第二个区间,右边.

非旋转Treap详解

利用其他人其中考试的时间,终于学完了非旋转Treap,它与普通Treap的区别就是它不旋转废话.前置知识只有BST和可并堆. BST看这个博客,解释的挺清楚的.https://www.cnblogs.com/jiangminghong/p/9999884.html 可并堆就是用很快的时间合并两个堆.如果裸上一个并查集的话就是nlog2n.这个复杂度我们是不能接受的.正常的可并堆是维护一棵左偏树,我们用一个参数dis[x]表示从x点出发能够向右走的最大步数.每次两个堆合并时,我们就把一个堆扔到另一

luoguP5055 【模板】可持久化文艺平衡树 可持久化非旋转treap

好题. Code: #include<bits/stdc++.h> using namespace std; #define setIO(s) freopen(s".in","r",stdin) namespace fhqtreap{ #define maxn 20000000 #define ll long long int tot,m,tr; int trash[maxn]; int ls[maxn],rs[maxn],rev[maxn],val[m

斜堆,非旋转treap,替罪羊树

一.斜堆 斜堆是一种可以合并的堆 节点信息: struct Node { int v; Node *ch[2]; }; 主要利用merge函数 Node *merge(Node *x, Node *y) { if(!x) return y; if(!y) return x; if(x->v < y->v) swap(x, y); x->ch[1] = merge(x->ch[1], y); return swap(x->ch[0], x->ch[1]), x; }