不得不说平衡树博大精深,除了Treap,还有splay,非旋Treap和可持久化数据结构,今天先讲讲Treap,也很感谢这位大佬的博客给予我帮助:http://www.360doc.com/content/19/0120/11/5315_810146183.shtml

Treap的核心就是Tree+Heap,即在二叉搜索树的基础上根据随机数生成的优先级使树保持堆的性质,从而实现使Treap的深度不会太大的效果

核心操作就是旋转:人工YY一下……发现旋转有左旋(Zag)和右旋(Zig)两种操作,旋转时连三条边,即原顶点和新儿子的边,新定点作为原顶点父亲的边,以及上一级父亲连向新顶点的边。顶点和儿子连接的边先处理,用&p始终维护顶点坐标,因为传入的是son[father[p]],所以p变化时它父亲的儿子会自动变化,所以通过取址可以自动连边 (rotate下一层的p是上一层的son,p变上一层son就变) (详见代码)

除了insert和delete操作中需要rotate(delete要把删除点转到叶子结点再删去,insert在插入后要旋转以维护heap的性质,不要忘记操作之后要push_up),其它的代码都和二叉搜索树一样

模板题:

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=;
const int N=;
const int inf=(int)2e9;
int R=; int ran()
{
static ll seed=;
seed=seed*%mod;
return (int)seed;
} struct node
{
int rnd,val,sz,ch[],num;
node(){}
node(int val,int sz,int num):val(val),sz(sz),num(num){
rnd=ran();//大根堆
ch[]=ch[]=;//和上方函数不能同时使用,若去掉该句会MLE
}
}a[];
int sum_node=;//同一节点内的相同数字的个数 void push_up(int x)
{
a[x].sz=a[x].num+a[a[x].ch[]].sz+a[a[x].ch[]].sz;
} void rotate(int &p,int d)//0:Zag 1:Zig
{
int k=a[p].ch[d^];
a[p].ch[d^]=a[k].ch[d];
a[k].ch[d]=p;
push_up(p);
push_up(k);
p=k;//传入的是son[father],father连边自动变
} void insert(int &p,int x)
{
if(!p)
{
p=++sum_node;
node tmp(x,,);
a[p]=tmp;
return;
}
if(a[p].val==x)
{
a[p].num++;
a[p].sz++;
return;
}
int d=(int)(x>a[p].val);
insert(a[p].ch[d],x);
if(a[p].rnd<a[a[p].ch[d]].rnd) rotate(p,d^);//大根堆
//本一级rotate传入的p就是上一级insert传入的son
push_up(p);
return;
} void del(int &p,int x)
{
if(!p) return;
if(a[p].val>x) del(a[p].ch[],x);
else if(a[p].val<x) del(a[p].ch[],x);
else
{
if(!a[p].ch[]&&!a[p].ch[])
{
a[p].num--; a[p].sz--;
if(a[p].num==) p=;
}
else if(a[p].ch[]&&!a[p].ch[])
{
rotate(p,);
del(a[p].ch[],x);
}
else if(!a[p].ch[]&&a[p].ch[])
{
rotate(p,);
del(a[p].ch[],x);//之前写反了
}
else
{
int d=(int)(a[a[p].ch[]].rnd>a[a[p].ch[]].rnd);
rotate(p,d);
del(a[p].ch[d],x);
}
}
push_up(p);
} int rnk(int p,int x)//以p为根的x的rank
{
if(p==) return ;//important
if(x>a[p].val)
{
return a[a[p].ch[]].sz+a[p].num+rnk(a[p].ch[],x);
}
else if(x<a[p].val)
{
return rnk(a[p].ch[],x);
}
else
{
return a[a[p].ch[]].sz+;
}
} int find(int p,int x)//已知rank查数
{
if(!p) return ;
if(a[a[p].ch[]].sz+a[p].num<x)//important(num)
{
return find(a[p].ch[],x-a[a[p].ch[]].sz-a[p].num);
}
else if(a[a[p].ch[]].sz>=x)//>=!!!
{
return find(a[p].ch[],x);
}
else
{
return a[p].val;
}
} int pre(int p,int x)
{
if(!p) return -inf;
if(a[p].val>=x)
{
return pre(a[p].ch[],x);
}
else return max(a[p].val,pre(a[p].ch[],x));
} int suc(int p,int x)
{
if(!p) return inf;
if(a[p].val<=x)
{
return suc(a[p].ch[],x);
}
else return min(a[p].val,suc(a[p].ch[],x));
} int n;
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
{
int opt,x;
scanf("%d%d",&opt,&x);
if(opt==) insert(R,x);//R每次会随着p改变
else if(opt==) del(R,x);
else if(opt==) printf("%d\n",rnk(R,x));
else if(opt==) printf("%d\n",find(R,x));
else if(opt==) printf("%d\n",pre(R,x));
else printf("%d\n",suc(R,x));
}
return ;
}

最新文章

  1. [Android Studio] *.jar 与 *.aar 的生成与*.aar导入项目方法
  2. SQL Server索引进阶第五篇:索引包含列 .
  3. Java 多线程(1)-Thread和Runnable
  4. coreData旧版本增加字段,新版本是否可以继续使用旧版本内容的测试(MagicalRecord的使用)
  5. 有关JSP注释
  6. jQuery实现的鼠标滑过切换图片代码实例
  7. ASP实现用年月日时分秒和两位随机数字来作为上传文件名的函数
  8. yii2的安装使用
  9. HDU 4276 The Ghost Blows Light(树形)
  10. DEVC++生成DLL的方法
  11. SAR图像与光学图像区别
  12. USACO Arithmetic Progressions 【构造等差数列】
  13. django perm用法
  14. jemeter逻辑控制器
  15. CF 508D Tanya and Password(无向图+输出欧拉路)
  16. kvm的live-snapshot
  17. java学习(二)--excel导出
  18. JS是按值传递还是按引用传递?【转载】
  19. 【NOIP2014提高组】寻找道路
  20. CentOS 6.5 搭建 Zabbix

热门文章

  1. flutter setInitialRoute: 不生效
  2. pop&amp;dismiss
  3. 随笔记录 linux命令 2019.7.29
  4. Bootstrap 附加导航(Affix)插件
  5. ERROR 1872
  6. ZuulFilter
  7. 线性推概率——cf1009E好题!
  8. LUOGU P1402 酒店之王 (网络流)
  9. 手机端判断安卓,iso,微信
  10. C#获取当前运行的源代码的文件名和当前源代码的行数的方法