P3369 【模板】普通平衡树

平衡树大法好,蒟蒻(博主)最近正在收集高级数据结构的碎片,企图合成数据结构的元素之力来使自己的RP++。。。

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

插入xx数
删除xx数(若有多个相同的数,因只删除一个)
查询xx数的排名(排名定义为比当前数小的数的个数+1+1。若有多个相同的数,因输出最小的排名)
查询排名为xx的数
求xx的前驱(前驱定义为小于xx,且最大的数)
求xx的后继(后继定义为大于xx,且最小的数)

教程地址

博主太蒟,只能抄模板,曾经抄了个假的模板,有点=_=

让我们来看一看splay是如何进行神奇维护的吧!

  1. int ch[N][],cnt[N],val[N],size[N],par[N],root,ncnt;
    //左右儿子,重复值,数的值,子树大小,par父节点,根,出现过的节点数
  1. int chk(int x){
  2. return ch[par[x]][]==x;
  3. }//返回x是其父节点的左儿子,还是右儿子
  1. void pushup(int x){
  2. size[x]=size[ch[x][]]+size[ch[x][]]+cnt[x];
  3. }//维护x的size值,除了子树还要加上自身的重复值
  1. //著名的rotate操作
    void rotate(int x){
  2. int y=par[x],z=par[y],k=chk(x),w=ch[x][k^];
  3. ch[y][k]=w,par[w]=y;//父亲
  4. ch[z][chk(y)]=x,par[x]=z;//祖父
  5. ch[x][k^]=y,par[y]=x;//自己
  6. pushup(y),pushup(x);
  7. }
    //交换儿子和父节点的位置,随之ch,par要改变
  1. //splay吗,伸展操作
    void splay(int x,int goal=){
  2. while(par[x]!=goal){
  3. int y=par[x],z=par[y];
  4. if(z!=goal){
  5. if(chk(y)==chk(x)) rotate(y);
  6. else rotate(x);
  7. }
  8. rotate(x);
  9. }
    //分情况讨论,这样才能使树的深度尽量小
  10. if(!goal) root=x;
  11. }//将x旋转到根,或goal的儿子
  1. //find操作,将最大的小于等于x的子树旋转到根
    void find(int x){
  2. // if(!root) return;
  3. int cur=root;
  4. while(ch[cur][x>val[cur]]&&x!=val[cur]){
  5. cur=ch[cur][x>val[cur]];
  6. }
  7. splay(cur);
  8. }
  1. //插入操作
    void insert(int x){
  2. int cur=root,p=;
  3. while(cur&&val[cur]!=x){
  4. p=cur;
  5. cur=ch[cur][x>val[cur]];
  6. }
  7. if(cur){
  8. cnt[cur]++;
  9. }else{
  10. cur=++ncnt;
  11. if(p) ch[p][x>val[p]]=cur;
  12. ch[cur][]=ch[cur][]=;
  13. val[cur]=x,par[cur]=p;
  14. cnt[cur]=size[cur]=;
  15. }
  16. splay(cur);
  17. }
  1. 查找第k大的数
    int kth(int k){
  2. int cur=root;
  3. while(){
  4. if(ch[cur][]&&k<=size[ch[cur][]]){//前提是它拥有左子树
  5. cur=ch[cur][];
  6. }else if(k>size[ch[cur][]]+cnt[cur]){
  7. k-=size[ch[cur][]]+cnt[cur];//注意还要删去重复值
  8. cur=ch[cur][];
  9. }else return cur;
  10. }
  11. }
  1. int pre(int x){//查找前驱
  2. find(x);
  3. if(val[root]<x) return root;
  4. int cur=ch[root][];//返回左子树最右边的节点
  5. while(ch[cur][]){
  6. cur=ch[cur][];
  7. }
  8. return cur;
  9. }
  10. int succ(int x){
  11. find(x);
  12. if(val[root]>x) return root;
  13. int cur=ch[root][];//返回右子树最左边的节点
  14. while(ch[cur][]){
  15. cur=ch[cur][];
  16. }
  17. return cur;
  18. }
  1. int remove(int x){
  2. int last=pre(x),next=succ(x);
  3. splay(last);splay(next,last);
  4. int del=ch[next][];
  5. if(cnt[del]>){
  6. cnt[del]--;
  7. splay(del);
  8. }else ch[next][]=;
  9. }

因为一个点前驱和后继之间只有自己本身

那么可以考虑把前驱splay到根,再把后继splay到前驱的右子树,那么后继的左儿子就是要删除的点。

  1. #include<iostream>
  2. #include<cstdio>
  3.  
  4. #define N 10000000
  5. using namespace std;
  6.  
  7. int ch[N][],cnt[N],val[N],size[N],par[N],root,ncnt;
  8.  
  9. int chk(int x){
  10. return ch[par[x]][]==x;
  11. }
  12.  
  13. void pushup(int x){
  14. size[x]=size[ch[x][]]+size[ch[x][]]+cnt[x];
  15. }
  16.  
  17. void rotate(int x){
  18. int y=par[x],z=par[y],k=chk(x),w=ch[x][k^];
  19. ch[y][k]=w,par[w]=y;//父亲
  20. ch[z][chk(y)]=x,par[x]=z;//祖父
  21. ch[x][k^]=y,par[y]=x;//自己
  22. pushup(y),pushup(x);
  23. }
  24.  
  25. void splay(int x,int goal=){
  26. while(par[x]!=goal){
  27. int y=par[x],z=par[y];
  28. if(z!=goal){
  29. if(chk(y)==chk(x)) rotate(y);
  30. else rotate(x);
  31. }
  32. rotate(x);
  33. }
  34. if(!goal) root=x;
  35. }
  36.  
  37. void find(int x){
  38. // if(!root) return;
  39. int cur=root;
  40. while(ch[cur][x>val[cur]]&&x!=val[cur]){
  41. cur=ch[cur][x>val[cur]];
  42. }
  43. splay(cur);
  44. }
  45.  
  46. void insert(int x){
  47. int cur=root,p=;
  48. while(cur&&val[cur]!=x){
  49. p=cur;
  50. cur=ch[cur][x>val[cur]];
  51. }
  52. if(cur){
  53. cnt[cur]++;
  54. }else{
  55. cur=++ncnt;
  56. if(p) ch[p][x>val[p]]=cur;
  57. ch[cur][]=ch[cur][]=;
  58. val[cur]=x,par[cur]=p;
  59. cnt[cur]=size[cur]=;
  60. }
  61. splay(cur);
  62. }
  63.  
  64. int kth(int k){
  65. int cur=root;
  66. while(){
  67. if(ch[cur][]&&k<=size[ch[cur][]]){
  68. cur=ch[cur][];
  69. }else if(k>size[ch[cur][]]+cnt[cur]){
  70. k-=size[ch[cur][]]+cnt[cur];
  71. cur=ch[cur][];
  72. }else return cur;
  73. }
  74. }
  75.  
  76. int pre(int x){
  77. find(x);
  78. if(val[root]<x) return root;
  79. int cur=ch[root][];
  80. while(ch[cur][]){
  81. cur=ch[cur][];
  82. }
  83. return cur;
  84. }
  85. int succ(int x){
  86. find(x);
  87. if(val[root]>x) return root;
  88. int cur=ch[root][];
  89. while(ch[cur][]){
  90. cur=ch[cur][];
  91. }
  92. return cur;
  93. }
  94.  
  95. void remove(int x){
  96. int last=pre(x),next=succ(x);
  97. splay(last);splay(next,last);
  98. int del=ch[next][];
  99. if(cnt[del]>){
  100. cnt[del]--;
  101. splay(del);
  102. }else ch[next][]=;
  103. }
  104.  
  105. int n;
  106.  
  107. int main()
  108. {
  109. scanf("%d",&n);
  110. insert(0x3f3f3f3f);
  111. insert(0xcfcfcfcf);
  112. for(int opt,x,i=;i<=n;i++){
  113. scanf("%d%d",&opt,&x);
  114. if(opt==) insert(x);
  115. if(opt==) remove(x);
  116. if(opt==) find(x),printf("%d\n",size[ch[root][]]);
  117. if(opt==) printf("%d\n",val[kth(x+)]);
  118. if(opt==) printf("%d\n",val[pre(x)]);
  119. if(opt==) printf("%d\n",val[succ(x)]);
  120. }
  121.  
  122. return ;
  123. }

2018.10.11

打板日记——错误笔记

  1. #include<iostream>
  2. #include<cmath>
  3. #include<cstdio>
  4. #include<algorithm>
  5.  
  6. #define N 10000000
  7. #define IL inline
  8. using namespace std;
  9.  
  10. int ch[N][],par[N],siz[N],ncnt,root,val[N],cnt[N];
  11.  
  12. IL int chk(int x){
  13. return ch[par[x]][]==x;
  14. }
  15. IL void pushup(int x){
  16. siz[x]=siz[ch[x][]]+siz[ch[x][]]+cnt[x];
  17. }
  18. IL void rotate(int x){
  19. int y=par[x],z=par[y],k=chk(x),w=ch[x][k^];
  20. ch[y][k]=w,par[w]=y;
  21. ch[z][chk(y)]=x,par[x]=z;
  22. ch[x][k^]=y,par[y]=x;
  23. pushup(y),pushup(x);
  24. }
  25. IL void splay(int x,int goal=){
  26. while(par[x]!=goal){
  27. int y=par[x],z=par[y];
  28. if(z!=goal){
  29. if(chk(x)==chk(y)) rotate(y);
  30. else rotate(x);
  31. }
  32. rotate(x);
  33. }
  34. if(!goal) root=x;
  35. }
  36. IL void find(int x){
  37. int cur=root;
  38. while(x!=val[cur]&&ch[cur][x>val[cur]]){//将最大的<=x的数所在的节点splay到根
  39. cur=ch[cur][x>val[cur]];
  40. }
  41. splay(cur);
  42. }
  43. IL void insert(int x){
  44. int cur=root,p=;
  45. while(cur&&x!=val[cur]){//前提是cur是真,即存在这个节点
  46. p=cur;
  47. cur=ch[cur][x>val[cur]];
  48. }
  49. if(cur){
  50. cnt[cur]++;
  51. }else{
  52. cur=++ncnt;
  53. if(p) ch[p][x>val[p]]=cur;
  54. ch[cur][]=ch[cur][]=;
  55. siz[cur]=cnt[cur]=;
  56. par[cur]=p,val[cur]=x;
  57. }
  58. splay(cur);
  59. }
  60. IL int pre(int x){
  61. find(x);
  62. if(val[root]<x) return root;
  63. int cur=ch[root][];
  64. while(ch[cur][]){//返回左子树最右边的节点
  65. cur=ch[cur][];
  66. }
  67. return cur;
  68. }
  69. IL int succ(int x){
  70. find(x);
  71. if(val[root]>x) return root;
  72. int cur=ch[root][];
  73. while(ch[cur][]){//返回右子树最左边的节点
  74. cur=ch[cur][];
  75. }
  76. return cur;
  77. }
  78. IL int kth(int k){
  79. int cur=root;
  80. while(){
  81. if(ch[cur][]&&k<=siz[ch[cur][]]){//前提是他拥有左子树
  82. cur=ch[cur][];
  83. }else if(k>siz[ch[cur][]]+cnt[cur]){
  84. k-=siz[ch[cur][]]+cnt[cur];//注意细节,还要减去自身的重复值
  85. cur=ch[cur][];
  86. }else return cur;
  87. }
  88. }
  89.  
  90. IL void remove(int x){
  91. int last=pre(x),next=succ(x);
  92. splay(last),splay(next,last);
  93. int del=ch[next][];//并不是根的左子树,删除的是后继的左儿子
  94. if(cnt[del]>){
  95. cnt[del]--;
  96. splay(del);
  97. }else ch[next][]=;
  98. }
  99.  
  100. int n;
  101.  
  102. int main()
  103. {
  104. scanf("%d",&n);
  105. insert(0x3f3f3f3f);
  106. insert(0xcfcfcfcf);
  107. for(int opt,x,i=;i<=n;i++){
  108. scanf("%d%d",&opt,&x);
  109. if(opt==) insert(x);
  110. if(opt==) remove(x);
  111. if(opt==) find(x),printf("%d\n",siz[ch[root][]]);
  112. if(opt==) printf("%d\n",val[kth(x+)]);
  113. if(opt==) printf("%d\n",val[pre(x)]);
  114. if(opt==) printf("%d\n",val[succ(x)]);
  115. }
  116.  
  117. return ;
  118. }

emmmm

splay另一个骚操作请转emmm

STL or 01trie

洛谷——P3369 【模板】普通平衡树(splay)(基础splay,维护一些神奇的东东)的更多相关文章

  1. 【洛谷P3369】普通平衡树——Splay学习笔记(一)

    二叉搜索树(二叉排序树) 概念:一棵树,若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值: 它的左.右子树也分别为二叉搜索树 ...

  2. 洛谷.3369.[模板]普通平衡树(Splay)

    题目链接 第一次写(2017.11.7): #include<cstdio> #include<cctype> using namespace std; const int N ...

  3. 洛谷.3391.[模板]文艺平衡树(Splay)

    题目链接 //注意建树 #include<cstdio> #include<algorithm> const int N=1e5+5; //using std::swap; i ...

  4. 洛谷.3369.[模板]普通平衡树(fhq Treap)

    题目链接 第一次(2017.12.24): #include<cstdio> #include<cctype> #include<algorithm> //#def ...

  5. 洛谷P3369 【模板】普通平衡树(Treap/SBT)

    洛谷P3369 [模板]普通平衡树(Treap/SBT) 平衡树,一种其妙的数据结构 题目传送门 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除 ...

  6. 【洛谷P3369】【模板】普通平衡树题解

    [洛谷P3369][模板]普通平衡树题解 题目链接 题意: 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3 ...

  7. 绝对是全网最好的Splay 入门详解——洛谷P3369&BZOJ3224: Tyvj 1728 普通平衡树 包教包会

    平衡树是什么东西想必我就不用说太多了吧. 百度百科: 一个月之前的某天晚上,yuli巨佬为我们初步讲解了Splay,当时接触到了平衡树里的旋转等各种骚操作,感觉非常厉害.而第二天我调Splay的模板竟 ...

  8. 洛谷P3369普通平衡树(Treap)

    题目传送门 转载自https://www.cnblogs.com/fengzhiyuan/articles/7994428.html,转载请注明出处 Treap 简介 Treap 是一种二叉查找树.它 ...

  9. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

随机推荐

  1. AndroidStudio更改Gradle的版本

    1.首先需要在Gradle官网上下载需要的gradle版本,对于imac需要放置到AndroidStudio的安装目录下的gradle目录下面 2.更改项目的build.gradle的gradle的版 ...

  2. iOS如何查看静态库.a文件支持的cpu类型

    打开终端: 输入 lipo -info 然后将你要查看的静态库.a 文件找到,拖入 -info 后边.假设路径为A,即为 lipo -info A 回车键,然后就会看到静态库是否支持 armv7,ar ...

  3. 【POI】T1 特工 szp

    T1 特工szp [问题描述] Byteotian 中央情报局 (BIA) 雇佣了许多特工. 他们每个人的工作就是监视另一名特工.Byteasar 国王需要进行一次秘密行动,所以他要挑选尽量多的信得过 ...

  4. Dinic(模板 再错是不可能的 这辈子都不可能了)

    #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #inclu ...

  5. SVN工具使用技巧

    SVN打tag SVN打tag是一个很常用的功能,要谈打tag,还得从SVN官方推荐的目录结构说起.SVN官方推荐在一个版本库的根目录下先建立trunk.branches.tags这三个文件夹,其中t ...

  6. 错误: 实例 "ruiy" 执行所请求操作失败,实例处于错误状态。: 请稍后再试 [错误: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)].

    错误: 实例 "ruiy" 执行所请求操作失败,实例处于错误状态.: 请稍后再试 [错误: 'ascii' codec can't decode byte 0xe6 in posi ...

  7. 1642: [Usaco2007 Nov]Milking Time 挤奶时间(dp)

    1642: [Usaco2007 Nov]Milking Time 挤奶时间 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 935  Solved: 55 ...

  8. LoadRunner监控Linux配置教程

    LoadRunner监控Linux资源时弹出如下错误: Monitor name :UNIX Resources. Cannot initialize the monitoring on 192.16 ...

  9. GG_Logs 日志类库封装使用说明

    3.6.GG_Logs 日志类库封装使用说明 GG_Logs类库项目,Nuget安装log4net 添加代码配置代码: [assembly: log4net.Config.XmlConfigurato ...

  10. 贪心 CodeForces 137B Permutation

    题目传送门 /* 贪心:因为可以任意修改,所以答案是没有出现过的数字的个数 */ #include <cstdio> #include <algorithm> #include ...