题目:

此为平衡树系列第一道:普通平衡树您需要写一种数据结构,来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

n<=100000 所有数字均在-107到107内。

输入样例:

  1. 10
  2. 1 106465
  3. 4 1
  4. 1 317721
  5. 1 460929
  6. 1 644985
  7. 1 84185
  8. 1 89851
  9. 6 81968
  10. 1 492737
  11. 5 493598
输出样例:

  1. 106465
  2. 84185
  3. 492737

变量声明:size[x],以x为根节点的子树大小;ls[x],x的左儿子;rs[x],x的右子树;r[x],x节点的随机数;v[x],x节点的权值;w[x],x节点所对应的权值的数的个数。

root,树的总根;tot,树的大小。

treap是tree(树)和heap(堆)的组合词,顾名思义就是在树上建堆,所以treap满足堆的性质,但treap又是一个平衡树所以也满足平衡树的性质(对于每个点,它的左子树上所有点都比它小,它的右子树上所有点都比他大,故平衡树的中序遍历就是树上所有点点权的顺序数列)。

先介绍几个基本旋转treap操作:

1.左旋和右旋

左旋即把Q旋到P的父节点,右旋即把P旋到Q的父节点。

以右旋为例:因为Q>B>P所以在旋转之后还要满足平衡树性质所以B要变成Q的左子树。在整个右旋过程中只改变了B的父节点,P的右节点和父节点,Q的左节点的父节点,与A,B,C的子树无关。

  1. void rturn(int &x)
  2. {
  3. int t;
  4. t=ls[x];
  5. ls[x]=rs[t];
  6. rs[t]=x;
  7. size[t]=size[x];
  8. up(x);
  9. x=t;
  10. }
  11. void lturn(int &x)
  12. {
  13. int t;
  14. t=rs[x];
  15. rs[x]=ls[t];
  16. ls[t]=x;
  17. size[t]=size[x];
  18. up(x);
  19. x=t;
  20. }

2.查询

我们以查询权值为x的点为例,从根节点开始走,判断x与根节点权值大小,如果x大就向右下查询,比较x和根右儿子大小;如果x小就向左下查询,直到查询到等于x的节点或查询到树的最底层。

3.插入

插入操作就是遵循平衡树性质插入到树中。对于要插入的点x和当前查找到的点p,判断x与p的大小关系。注意在每次向下查找时因为要保证堆的性质,所以要进行左旋或右旋。

  1. void insert_sum(int x,int &i)
  2. {
  3. if(!i)
  4. {
  5. i=++tot;
  6. w[i]=size[i]=1;
  7. v[i]=x;
  8. r[i]=rand();
  9. return ;
  10. }
  11. size[i]++;
  12. if(x==v[i])
  13. {
  14. w[i]++;
  15. }
  16. else if(x>v[i])
  17. {
  18. insert_sum(x,rs[i]);
  19. if(r[rs[i]]<r[i])
  20. {
  21. lturn(i);
  22. }
  23. }
  24. else
  25. {
  26. insert_sum(x,ls[i]);
  27. if(r[ls[i]]<r[i])
  28. {
  29. rturn(i);
  30. }
  31. }
  32.  
  33. return ;
  34. }

4.上传

每次旋转后因为子树有变化所以要修改父节点的子树大小。

  1. void up(int x)
  2. {
  3. size[x]=size[rs[x]]+size[ls[x]]+w[x];
  4. }

5.删除

删除节点的方法和堆类似,要把点旋到最下层再删,如果一个节点w不是1那就把w--就行。

  1. void delete_sum(int x,int &i)
  2. {
  3. if(i==0)
  4. {
  5. return ;
  6. }
  7. if(v[i]==x)
  8. {
  9. if(w[i]>1)
  10. {
  11. w[i]--;
  12. size[i]--;
  13. return ;
  14. }
  15. if((ls[i]*rs[i])==0)
  16. {
  17. i=ls[i]+rs[i];
  18. }
  19. else if(r[ls[i]]<r[rs[i]])
  20. {
  21. rturn(i);
  22. delete_sum(x,i);
  23. }
  24. else
  25. {
  26. lturn(i);
  27. delete_sum(x,i);
  28. }
  29. return ;
  30. }
  31. size[i]--;
  32. if(v[i]<x)
  33. {
  34. delete_sum(x,rs[i]);
  35. }
  36. else
  37. {
  38. delete_sum(x,ls[i]);
  39. }
  40. return ;
  41. }

6.查找排名

查找操作和上面说的差不多,只不过要注意当查找一个节点右子树时要把答案加上这个点的w和这个节点左子树的size。

  1. int ask_num(int x,int i)
  2. {
  3. if(i==0)
  4. {
  5. return 0;
  6. }
  7. if(v[i]==x)
  8. {
  9. return size[ls[i]]+1;
  10. }
  11. if(v[i]<x)
  12. {
  13. return ask_num(x,rs[i])+size[ls[i]]+w[i];
  14. }
  15. return ask_num(x,ls[i]);
  16. }

7.查找权值

和查找排名差不多,查找右子树时要将所查找排名减掉父节点w和父节点的左子树的size。

  1. int ask_sum(int x,int i)
  2. {
  3. if(i==0)
  4. {
  5. return 0;
  6. }
  7. if(x>size[ls[i]]+w[i])
  8. {
  9. return ask_sum(x-size[ls[i]]-w[i],rs[i]);
  10. }
  11. else if(size[ls[i]]>=x)
  12. {
  13. return ask_sum(x,ls[i]);
  14. }
  15. else
  16. {
  17. return v[i];
  18. }
  19. }

8.查找前驱/后继

直接判断大小查询就好了qwq

前驱

  1. void ask_front(int x,int i)
  2. {
  3. if(i==0)
  4. {
  5. return ;
  6. }
  7. if(v[i]<x)
  8. {
  9. answer=i;
  10. ask_front(x,rs[i]);
  11. return ;
  12. }
  13. else
  14. {
  15. ask_front(x,ls[i]);
  16. return ;
  17. }
  18. return ;
  19. }

后继

  1. void ask_back(int x,int i)
  2. {
  3. if(i==0)
  4. {
  5. return ;
  6. }
  7. if(v[i]>x)
  8. {
  9. answer=i;
  10. ask_back(x,ls[i]);
  11. return ;
  12. }
  13. else
  14. {
  15. ask_back(x,rs[i]);
  16. return ;
  17. }
  18. }

最后附上完整代码(虽然有点长但自认为很好理解也很详细。。。)

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cmath>
  4. #include<cstring>
  5. #include<iostream>
  6. #include<ctime>
  7. using namespace std;
  8. int n;
  9. int opt;
  10. int x;
  11. int size[100001];
  12. int rs[100001];
  13. int ls[100001];
  14. int v[100001];
  15. int w[100001];
  16. int r[100001];
  17. int tot;
  18. int root;
  19. int answer;
  20. void up(int x)
  21. {
  22. size[x]=size[rs[x]]+size[ls[x]]+w[x];
  23. }
  24. void rturn(int &x)
  25. {
  26. int t;
  27. t=ls[x];
  28. ls[x]=rs[t];
  29. rs[t]=x;
  30. size[t]=size[x];
  31. up(x);
  32. x=t;
  33. }
  34. void lturn(int &x)
  35. {
  36. int t;
  37. t=rs[x];
  38. rs[x]=ls[t];
  39. ls[t]=x;
  40. size[t]=size[x];
  41. up(x);
  42. x=t;
  43. }
  44. void insert_sum(int x,int &i)
  45. {
  46. if(!i)
  47. {
  48. i=++tot;
  49. w[i]=size[i]=1;
  50. v[i]=x;
  51. r[i]=rand();
  52. return ;
  53. }
  54. size[i]++;
  55. if(x==v[i])
  56. {
  57. w[i]++;
  58. }
  59. else if(x>v[i])
  60. {
  61. insert_sum(x,rs[i]);
  62. if(r[rs[i]]<r[i])
  63. {
  64. lturn(i);
  65. }
  66. }
  67. else
  68. {
  69. insert_sum(x,ls[i]);
  70. if(r[ls[i]]<r[i])
  71. {
  72. rturn(i);
  73. }
  74. }
  75. return ;
  76. }
  77. void delete_sum(int x,int &i)
  78. {
  79. if(i==0)
  80. {
  81. return ;
  82. }
  83. if(v[i]==x)
  84. {
  85. if(w[i]>1)
  86. {
  87. w[i]--;
  88. size[i]--;
  89. return ;
  90. }
  91. if((ls[i]*rs[i])==0)
  92. {
  93. i=ls[i]+rs[i];
  94. }
  95. else if(r[ls[i]]<r[rs[i]])
  96. {
  97. rturn(i);
  98. delete_sum(x,i);
  99. }
  100. else
  101. {
  102. lturn(i);
  103. delete_sum(x,i);
  104. }
  105. return ;
  106. }
  107. size[i]--;
  108. if(v[i]<x)
  109. {
  110. delete_sum(x,rs[i]);
  111. }
  112. else
  113. {
  114. delete_sum(x,ls[i]);
  115. }
  116. return ;
  117. }
  118. int ask_num(int x,int i)
  119. {
  120. if(i==0)
  121. {
  122. return 0;
  123. }
  124. if(v[i]==x)
  125. {
  126. return size[ls[i]]+1;
  127. }
  128. if(v[i]<x)
  129. {
  130. return ask_num(x,rs[i])+size[ls[i]]+w[i];
  131. }
  132. return ask_num(x,ls[i]);
  133. }
  134. int ask_sum(int x,int i)
  135. {
  136. if(i==0)
  137. {
  138. return 0;
  139. }
  140. if(x>size[ls[i]]+w[i])
  141. {
  142. return ask_sum(x-size[ls[i]]-w[i],rs[i]);
  143. }
  144. else if(size[ls[i]]>=x)
  145. {
  146. return ask_sum(x,ls[i]);
  147. }
  148. else
  149. {
  150. return v[i];
  151. }
  152. }
  153. void ask_front(int x,int i)
  154. {
  155. if(i==0)
  156. {
  157. return ;
  158. }
  159. if(v[i]<x)
  160. {
  161. answer=i;
  162. ask_front(x,rs[i]);
  163. return ;
  164. }
  165. else
  166. {
  167. ask_front(x,ls[i]);
  168. return ;
  169. }
  170. return ;
  171. }
  172. void ask_back(int x,int i)
  173. {
  174. if(i==0)
  175. {
  176. return ;
  177. }
  178. if(v[i]>x)
  179. {
  180. answer=i;
  181. ask_back(x,ls[i]);
  182. return ;
  183. }
  184. else
  185. {
  186. ask_back(x,rs[i]);
  187. return ;
  188. }
  189. }
  190. int main()
  191. {
  192. srand(12378);
  193. scanf("%d",&n);
  194. for(int i=1;i<=n;i++)
  195. {
  196. answer=0;
  197. scanf("%d%d",&opt,&x);
  198. if(opt==1)
  199. {
  200. insert_sum(x,root);
  201. }
  202. else if(opt==2)
  203. {
  204. delete_sum(x,root);
  205. }
  206. else if(opt==3)
  207. {
  208. printf("%d\n",ask_num(x,root));
  209. }
  210. else if(opt==4)
  211. {
  212. printf("%d\n",ask_sum(x,root));
  213. }
  214. else if(opt==5)
  215. {
  216. ask_front(x,root);
  217. printf("%d\n",v[answer]);
  218. }
  219. else if(opt==6)
  220. {
  221. ask_back(x,root);
  222. printf("%d\n",v[answer]);
  223. }
  224. }
  225. return 0;
  226. }

BZOJ3224普通平衡树——旋转treap的更多相关文章

  1. [BZOJ3224]普通平衡树(旋转treap,STL-vector)

    3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 20328  Solved: 8979[Submit][St ...

  2. BZOJ3224普通平衡树——非旋转treap

    题目: 此为平衡树系列第一道:普通平衡树您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数, ...

  3. 【bzoj3224】Tyvj 1728 普通平衡树 01Trie姿势+平衡树的四种姿势 :splay,旋转Treap,非旋转Treap,替罪羊树

    直接上代码 正所谓 人傻自带大常数 平衡树的几种姿势:  AVL Red&Black_Tree 码量爆炸,不常用:SBT 出于各种原因,不常用. 常用: Treap 旋转 基于旋转操作和随机数 ...

  4. 平衡树及笛卡尔树讲解(旋转treap,非旋转treap,splay,替罪羊树及可持久化)

    在刷了许多道平衡树的题之后,对平衡树有了较为深入的理解,在这里和大家分享一下,希望对大家学习平衡树能有帮助. 平衡树有好多种,比如treap,splay,红黑树,STL中的set.在这里只介绍几种常用 ...

  5. BZOJ3223文艺平衡树——非旋转treap

    此为平衡树系列第二道:文艺平衡树您需要写一种数据结构,来维护一个有序数列,其中需要提供以下操作: 翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 ...

  6. BZOJ3729Gty的游戏——阶梯博弈+巴什博弈+非旋转treap(平衡树动态维护dfs序)

    题目描述 某一天gty在与他的妹子玩游戏.妹子提出一个游戏,给定一棵有根树,每个节点有一些石子,每次可以将不多于L的石子移动到父节点,询问将某个节点的子树中的石子移动到这个节点先手是否有必胜策略.gt ...

  7. BZOJ3224 Tyvj 1728 普通平衡树(Treap)

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  8. [bzoj3196][Tyvj1730]二逼平衡树_树套树_位置线段树套非旋转Treap/树状数组套主席树/权值线段树套位置线段树

    二逼平衡树 bzoj-3196 Tyvj-1730 题目大意:请写出一个维护序列的数据结构支持:查询给定权值排名:查询区间k小值:单点修改:查询区间内定值前驱:查询区间内定值后继. 注释:$1\le ...

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

    国际惯例的题面:这种维护排序序列,严格大于的进行操作的题都很套路......我们按照[0,k],(k,2k],(2k,inf)分类讨论一下就好.显然第一个区间的不会变化,第二个区间的会被平移进第一个区 ...

随机推荐

  1. 初窥RabbitMQ消息中间及SpringBoot整合

    一:RabbitMQ简介 RabbitMQ介绍 RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用. 消息中间件最主要的作用是解耦,中间件最标准 ...

  2. docker部署nginx

    1. 下载nginx [root@localhost my.Shells]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/ ...

  3. .netcore 堆栈调用方法小记

    背景 上午临近午饭时,公司同事反馈验证码被攻击灌水.我们匆忙查询验证码明细,对已频繁出现的IP插入黑名单,但IP仍然隔断时间频繁变动,不得已之下只能先封禁对应公司id的验证码发送功能.年初时候,专门对 ...

  4. .NET(C#)主流ORM总揽

    前言 在以前的一篇文章中,为大家分享了<什么是ORM?为什么用ORM?浅析ORM的使用及利弊>.那么,在目前的.NET(C#)的世界里,有哪些主流的ORM,SqlSugar,Dapper, ...

  5. Node+GitLab实现小程序CI系统

    为什么要实现自动部署 小程序开发迭代里,有以下几个个头痛的问题, 如何准确并快速的的把小程序上传去后台,并让测试人员进行测试? 测试同事找开发要二维码,效率较低 本地生成的二维码会出现携带本地代码.未 ...

  6. 内网IP外网IP的关联及访问互联网原理

    首先解释一下“内网”与“外网”的概念: 内网:即所说的局域网,比如学校的局域网,局域网内每台计算机的IP地址在本局域网内具有互异性,是不可重复的.但两个局域网内的内网IP可以有相同的. 外网:即互联网 ...

  7. Appium-超过60s的应用场景如何处理

    前言: 最近在搞appium自动化项目,遇到超过60s的应用场景时,总是报错报错.如何解决呢?见下文. 报错信息: 2018-05-21 14:03:42:253 - [HTTP] <-- PO ...

  8. jconsole & jvisualvm远程监视websphere服务器JVM的配置案

    jconsole是JDK里自带的一个工具,可以监测Java程序运行时所有对象的申请.释放等动作,将内存管理的所有信息进行统计.分析.可视化.我们可以根据这些信息判断程序是否有内存泄漏问题. 使用jco ...

  9. Open Live Writer安装教程

    配置步骤: 1.在菜单中选择"工具">"帐户",出现下面的画面: 2.点击"添加按钮",在出现的窗口中选择"其他日志服务&q ...

  10. Python之字符串操作

    一.字符串特点 内容不可修改 password=' #内容不可修改 二.字符串常用方法 1..strip()方法 去字符串两边的空格和换行符 print(password.strip()) #去掉字符 ...