BZOJ3224普通平衡树——旋转treap

时间:2022-08-28 19:44:42

题目:

此为平衡树系列第一道:普通平衡树您需要写一种数据结构,来维护一些数,其中需要提供以下操作:
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 84185
1 89851
6 81968
1 492737
5 493598
输出样例:
106465
84185
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.左旋和右旋

BZOJ3224普通平衡树——旋转treap

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

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

void rturn(int &x)
{
int t;
t=ls[x];
ls[x]=rs[t];
rs[t]=x;
size[t]=size[x];
up(x);
x=t;
}
void lturn(int &x)
{
int t;
t=rs[x];
rs[x]=ls[t];
ls[t]=x;
size[t]=size[x];
up(x);
x=t;
}

2.查询

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

3.插入

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

void insert_sum(int x,int &i)
{
if(!i)
{
i=++tot;
w[i]=size[i]=1;
v[i]=x;
r[i]=rand();
return ;
}
size[i]++;
if(x==v[i])
{
w[i]++;
}
else if(x>v[i])
{
insert_sum(x,rs[i]);
if(r[rs[i]]<r[i])
{
lturn(i);
}
}
else
{
insert_sum(x,ls[i]);
if(r[ls[i]]<r[i])
{
rturn(i);
}
} return ;
}

4.上传

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

void up(int x)
{
size[x]=size[rs[x]]+size[ls[x]]+w[x];
}

5.删除

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

void delete_sum(int x,int &i)
{
if(i==0)
{
return ;
}
if(v[i]==x)
{
if(w[i]>1)
{
w[i]--;
size[i]--;
return ;
}
if((ls[i]*rs[i])==0)
{
i=ls[i]+rs[i];
}
else if(r[ls[i]]<r[rs[i]])
{
rturn(i);
delete_sum(x,i);
}
else
{
lturn(i);
delete_sum(x,i);
}
return ;
}
size[i]--;
if(v[i]<x)
{
delete_sum(x,rs[i]);
}
else
{
delete_sum(x,ls[i]);
}
return ;
}

6.查找排名

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

int ask_num(int x,int i)
{
if(i==0)
{
return 0;
}
if(v[i]==x)
{
return size[ls[i]]+1;
}
if(v[i]<x)
{
return ask_num(x,rs[i])+size[ls[i]]+w[i];
}
return ask_num(x,ls[i]);
}

7.查找权值

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

int ask_sum(int x,int i)
{
if(i==0)
{
return 0;
}
if(x>size[ls[i]]+w[i])
{
return ask_sum(x-size[ls[i]]-w[i],rs[i]);
}
else if(size[ls[i]]>=x)
{
return ask_sum(x,ls[i]);
}
else
{
return v[i];
}
}

8.查找前驱/后继

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

前驱

void ask_front(int x,int i)
{
if(i==0)
{
return ;
}
if(v[i]<x)
{
answer=i;
ask_front(x,rs[i]);
return ;
}
else
{
ask_front(x,ls[i]);
return ;
}
return ;
}

后继

void ask_back(int x,int i)
{
if(i==0)
{
return ;
}
if(v[i]>x)
{
answer=i;
ask_back(x,ls[i]);
return ;
}
else
{
ask_back(x,rs[i]);
return ;
}
}

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

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
#include<ctime>
using namespace std;
int n;
int opt;
int x;
int size[100001];
int rs[100001];
int ls[100001];
int v[100001];
int w[100001];
int r[100001];
int tot;
int root;
int answer;
void up(int x)
{
size[x]=size[rs[x]]+size[ls[x]]+w[x];
}
void rturn(int &x)
{
int t;
t=ls[x];
ls[x]=rs[t];
rs[t]=x;
size[t]=size[x];
up(x);
x=t;
}
void lturn(int &x)
{
int t;
t=rs[x];
rs[x]=ls[t];
ls[t]=x;
size[t]=size[x];
up(x);
x=t;
}
void insert_sum(int x,int &i)
{
if(!i)
{
i=++tot;
w[i]=size[i]=1;
v[i]=x;
r[i]=rand();
return ;
}
size[i]++;
if(x==v[i])
{
w[i]++;
}
else if(x>v[i])
{
insert_sum(x,rs[i]);
if(r[rs[i]]<r[i])
{
lturn(i);
}
}
else
{
insert_sum(x,ls[i]);
if(r[ls[i]]<r[i])
{
rturn(i);
}
}
return ;
}
void delete_sum(int x,int &i)
{
if(i==0)
{
return ;
}
if(v[i]==x)
{
if(w[i]>1)
{
w[i]--;
size[i]--;
return ;
}
if((ls[i]*rs[i])==0)
{
i=ls[i]+rs[i];
}
else if(r[ls[i]]<r[rs[i]])
{
rturn(i);
delete_sum(x,i);
}
else
{
lturn(i);
delete_sum(x,i);
}
return ;
}
size[i]--;
if(v[i]<x)
{
delete_sum(x,rs[i]);
}
else
{
delete_sum(x,ls[i]);
}
return ;
}
int ask_num(int x,int i)
{
if(i==0)
{
return 0;
}
if(v[i]==x)
{
return size[ls[i]]+1;
}
if(v[i]<x)
{
return ask_num(x,rs[i])+size[ls[i]]+w[i];
}
return ask_num(x,ls[i]);
}
int ask_sum(int x,int i)
{
if(i==0)
{
return 0;
}
if(x>size[ls[i]]+w[i])
{
return ask_sum(x-size[ls[i]]-w[i],rs[i]);
}
else if(size[ls[i]]>=x)
{
return ask_sum(x,ls[i]);
}
else
{
return v[i];
}
}
void ask_front(int x,int i)
{
if(i==0)
{
return ;
}
if(v[i]<x)
{
answer=i;
ask_front(x,rs[i]);
return ;
}
else
{
ask_front(x,ls[i]);
return ;
}
return ;
}
void ask_back(int x,int i)
{
if(i==0)
{
return ;
}
if(v[i]>x)
{
answer=i;
ask_back(x,ls[i]);
return ;
}
else
{
ask_back(x,rs[i]);
return ;
}
}
int main()
{
srand(12378);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
answer=0;
scanf("%d%d",&opt,&x);
if(opt==1)
{
insert_sum(x,root);
}
else if(opt==2)
{
delete_sum(x,root);
}
else if(opt==3)
{
printf("%d\n",ask_num(x,root));
}
else if(opt==4)
{
printf("%d\n",ask_sum(x,root));
}
else if(opt==5)
{
ask_front(x,root);
printf("%d\n",v[answer]);
}
else if(opt==6)
{
ask_back(x,root);
printf("%d\n",v[answer]);
}
}
return 0;
}

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

  1. &lbrack;BZOJ3224&rsqb;普通平衡树&lpar;旋转treap&comma;STL-vector&rpar;

    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姿势&plus;平衡树的四种姿势 :splay,旋转Treap,非旋转Treap,替罪羊树

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

  4. 平衡树及笛卡尔树讲解&lpar;旋转treap&comma;非旋转treap&comma;splay&comma;替罪羊树及可持久化&rpar;

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

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

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

  6. BZOJ3729Gty的游戏——阶梯博弈&plus;巴什博弈&plus;非旋转treap&lpar;平衡树动态维护dfs序&rpar;

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

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

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

  8. &lbrack;bzoj3196&rsqb;&lbrack;Tyvj1730&rsqb;二逼平衡树&lowbar;树套树&lowbar;位置线段树套非旋转Treap&sol;树状数组套主席树&sol;权值线段树套位置线段树

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

  9. 4923&colon; &lbrack;Lydsy1706月赛&rsqb;K小值查询 平衡树 非旋转Treap

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

随机推荐

  1. C&num; 热敏打印机 Socket 网络链接 打印 图片 (一)

    using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using Syste ...

  2. (续篇3):飞测独家のJmeter秘籍,限量发放

    好东西,分享大家,自上次分享出来fiddler导出jmx格式V4.0版本对外公开后,收到一些反馈,我们利用工作之余时间继续优化,现在一个比较稳定的版本出炉,分享给大伙,我们一起来看看. 特性说明: 版 ...

  3. 【转】Android 菜单&lpar;OptionMenu&rpar;大全 建立你自己的菜单--不错

    原文网址:http://www.cnblogs.com/salam/archive/2011/04/04/2005329.html 菜单是用户界面中最常见的元素之一,使用非常频繁,在Android中, ...

  4. hdoj 3555 Bomb&lpar;DFA&plus;dp&rpar;

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555 思路分析:该问题要求求解1—N中的数中含有49的数的个数,可以使用DFA来递推dp公式:详细解释 ...

  5. 在JavaScript中使用json&period;js:Ajax项目之GET请求(同步)

    1.用php编写一个提供数据的响应程序(phpdata.php) <?php $arr=array(1,2,3,4); //将数组编码为JSON格式的数据 $jsonarr=json_encod ...

  6. 【jQuery】 jQuery基础

    jQuery 之前在JS的文章中提到过,JS虽然功能全面但是仍然比较接近底层,代码写起来很麻烦,而以jQuery为代表的JS库包装了很多功能,可以让代码更加简单.接下来就来简单地记录一下我学习和所知道 ...

  7. 第12章 添加对外部认证的支持 - Identity Server 4 中文文档&lpar;v1&period;0&period;0&rpar;

    注意 对于任何先决条件(例如模板),首先要查看概述. 接下来,我们将添加对外部认证的支持.这非常简单,因为您真正需要的是ASP.NET Core兼容的身份验证处理程序. ASP.NET Core本身支 ...

  8. KNN分类算法补充

    KNN补充: 1.K值设定为多大? k太小,分类结果易受噪声点影响:k太大,近邻中又可能包含太多的其它类别的点. (对距离加权,可以降低k值设定的影响) k值通常是采用交叉检验来确定(以k=1为基准) ...

  9. python 条件语句和基础数据类型

    条件语句 if 条件: pass else: pass 如果1等于1,输出欢迎进入东京热,否则输出欢迎进入一本道 ==: print("欢迎进入东京热") else: print( ...

  10. 2017-2018-2 20165313实验二《Java面向对象程序设计》

    实验报告封面 实验内容及步骤 实验一 1.试验要求: 参考 (http://www.cnblogs.com/rocedu/p/6371315.html#SECUNITTEST) 完成单元测试的学习. ...