Treap和名次树
Treap名字的来源:Tree+Heap,正如名字一样,就是一颗简单的BST,一坨堆的合体。BST的不平衡的根本原因在于基于左<=根<=右的模式吃单调序列时候会无脑成长链,而Treap则添加一个优先级属性,值的大小随机生成,用最大堆的方式维护。之所以使用堆,是因为堆是一颗 完全二叉树,而BST梦寐以求的就是完全二叉结构,二者一结合,就产生了一种新的Balanced BST。Treap依赖于随机数,随机生成的优先级属性,通过简单的左右旋可以将长链旋转成近似完全二叉树结构,注意只是近似,平均情况下的树高是(log 1.46n),但是完全二叉是(log 2n),可以说还是有一点点差距的,不过基本接近红黑树的平衡状况。鉴于比红黑树好拍,而且可塑性很强(如果你是用STL红黑树),在不是很追求鲁棒性的前提下,Treap算是神器了。
常规的BST用不着Treap,Treap的一大作用是求第K大数,具体方法是每个结点维护一个附加信息s,s为所有子结点和自身结点的数量,形成一颗 Rank Tree(名次树),如果你是手拍红黑树,可以自己实现,用不着Treap。但是如果使用STL,STL高度封装特性使得我们无法定制自己的树,这点着实 蛋疼。记住Treap的优势:简化的可定制的红黑树!
先来介绍一下Treap的结点结构。
struct node
{
node *ch[];
int v,r,s; //v-value,r-random值,s-附加结点信息
int cmp(int x)
{
if(x==v) return -;
return x<v?:; //0-left,1-right
}
void maintain() //维护附加信息s
{
s=;
if(ch[]!=NULL) s+=ch[]->s;
if(ch[]!=NULL) s+=ch[]->s;
}
}
Treap的结点结构
Treap的主操作由以下几部分构成:rotate、insert,remove。
void rotate(node* &o,int d)
{
node *k=o->ch[d^];o->ch[d^]=k->ch[d];k->ch[d]=o;
o->maintain();k->maintain();o=k;
}
rotate操作
void insert(node* &o,int x)
{
if(o==NULL)
{o=new node;o->ch[]=o->ch[]=NULL;o->v=x;o->r=rand();o->s=;}
else
{
int d=x<o->v?:; //不使用cmp的原因:可能有重复元素
insert(o->ch[d],x);
if(o->ch[d]->r > o->r) rotate(o,d^);
}
o->maintain();
}
insert操作
void remove(node* &o,int x)
{
int d=o->cmp(x);
if(d==-)
{
if(o->ch[]!=NULL&&o->ch[]!=NULL)
{
int d2=o->ch[]->r>o->ch[]->r?:;
rotate(o,d2);
remove(o->ch[d2],x);
}
else {if(o->ch[]==NULL) o=o->ch[];else o=o->ch[];}
}
else remove(o->ch[d],x);
if(o!=NULL) o->maintain();
}
remove操作
remove的操作比较繁点,分为三种情况:
1.左右子树都存在,得让左右结点较大的r转到根,然后移出原来的根。
2.左右子树存其一,根直接改成左/右即可。
3.左右子树不存在,直接NULL即可。
然后就是RankTree的操作:kth
int kth(node *&o,int k)
{
if(o == NULL || k<= || k>o->s) return ;
int s = (o->ch[] == NULL ? : o->ch[]->s);
if(k == s + ) return o->v;
else if(k<=s) return kth(o->ch[],k);
else return kth(o->ch[],k-s-);
}
kth操作
@练习题
LA5031,查询指定点连通的第K大数。首先得明白连通≠邻接,所以查询某个点的连通点,得从该点的root进行。本题中虽然说是图,但是必须清 楚,图可以由多棵Tree合成。方法是先保持所有结点独立,各自为一颗Tree,然后对于有边的u、v,进行两颗Tree的合并操作。由于合并的时候要批 量修改父节点,这里借助于并查集完成操作。最麻烦的是删除指定一条边,如果正向处理,代码要写好多行。这里借助逆向操作,变删除为添加,就好办多了。注 意,一旦逆向后,原本修改结点的操作也要反过来。cmd里保存的是现有w的之前的w,然后覆盖w,逆向处理的时候修改的之前备份的值,而不是应该修改的 值。
主要操作为addedge,merge,change,query。
merge(src,dest): 土法子,就是把src所有点取出来insert。递归src左右子树,然后将src根的值insert到dest里面,最后src赋值NULL
addedge(u,v) :先确认合并的u、v是否在同一集合,如果不在,将结点少的点merge到结点多的点里面(优化)。
change(x,v): 先确定目标点的root,remove掉值为x的点(??对LRJ的代码产生疑惑,题目说是将指定id的点修改,万一有相同的x,那不就删了多点?) 然后insert值为v的点,修改w保存的值。
query: 每次通过并查集定位指定点的root,然后做kth,累加统计。
Treap和名次树的更多相关文章
- Treap 实现名次树
在主流STL版本中,set,map,都是BST实现的,具体来说是一种称为红黑树的动态平衡BST: 但是在竞赛中并不常用,因为红黑树过于复杂,他的插入 5 种,删除 6 中,代码量极大(如果你要改板子的 ...
- 模板——Treap实现名次树
Treap 是一种通过赋予结点随机权值的一种满足堆性质的二叉搜索树,它很好的解决了二叉搜索树顺序插入组成链式的局限性. 名次树是指在treap的每个结点中添加附加域size,表示以它为根的子树的总结点 ...
- 「模板」「讲解」Treap名次树
Treap实现名次树 前言 学平衡树的过程可以说是相当艰难.浏览Blog的过程中看到大量指针版平衡树,不擅长指针操作的我已经接近崩溃.于是,我想着一定要写一篇非指针实现的Treap的Blog. 具体如 ...
- bzoj3224 Tyvj 1728 普通平衡树(名次树+处理相同)
3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 5354 Solved: 2196[Submit][Sta ...
- uvalive 5031 Graph and Queries 名次树+Treap
题意:给你个点m条边的无向图,每个节点都有一个整数权值.你的任务是执行一系列操作.操作分为3种... 思路:本题一点要逆向来做,正向每次如果删边,复杂度太高.逆向到一定顺序的时候添加一条边更容易.详见 ...
- UVa 1479 (Treap 名次树) Graph and Queries
这题写起来真累.. 名次树就是多了一个附加信息记录以该节点为根的树的总结点的个数,由于BST的性质再根据这个附加信息,我们可以很容易找到这棵树中第k大的值是多少. 所以在这道题中用一棵名次树来维护一个 ...
- LA 5031 Graph and Queries —— Treap名次树
离线做法,逆序执行操作,那么原本的删除边的操作变为加入边的操作,用名次树维护每一个连通分量的名次,加边操作即是连通分量合并操作,每次将结点数小的子树向结点数大的子树合并,那么单次合并复杂度O(n1lo ...
- bzoj 3196 Tyvj 1730 二逼平衡树(线段树套名次树)
3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1807 Solved: 772[Submit][Stat ...
- UVaLive5031 Graph and Queries(时光倒流+名次树)
题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=20332 [思路] 时光倒流+名次树(rank tree). 所谓“ ...
随机推荐
- 2015安徽省赛 C.LU的困惑
题目描述 Master LU 非常喜欢数学,现在有个问题:在二维空间上一共有n个点,LU每连接两个点,就会确定一条直线,对应有一个斜率.现在LU把平面内所有点中任意两点连线,得到的斜率放入一个集合中( ...
- python中import和from...import...的区别
python中import和from...import...的区别: 只用import时,如import xx,引入的xx是模块名,而不是模块内具体的类.函数.变量等成员,使用该模块的成员时需写成xx ...
- 编译 uImage
编译 uImage 和测试 u-Boot - 小猪爱拱地 - 博客频道 - CSDN.NET http://blog.csdn.net/caspiansea/article/details/38057 ...
- MYSQL 删除字段值为NULL的语法
2014年9月1日 15:11:05 delete form your_table where your_field is null and your_field1 = '123' ...
- 使用eclipse开发Java web应用
前面说了手动配置一个应用,手动配置可以更深入的理解web应用的分布,但是一般的编辑器没有语法错误提示,所以开发起来对于错误的寻找不太容易,效率相对较低,所以在理解清楚web项目的结构之后,我们使用ec ...
- 【USACO】namenum
//开始傻×了 受题目形容的误导 一心想生成所有可能的 字符串组合 之后查找非常慢 //听了同学的 将5000个dict里的字符串 转换成char型数组(不能直接用int 会越界)直接用输入的数据对着 ...
- JavaScript、Jquery选择题
尚学堂Java EE软件工程师认证考试 试题库-选择题 一. 选择题(包括单选和双选) 1.B 在JavaScript中,以下变量命名非法的是( )(选择一项) A. numb_1 ...
- duapp获取mysql用户名密码等等……
duapp呵呵,又是云!比起新浪,这个免费.下面是:(你懂的) <?php/*数据库名称写自己创建的*/$dbname = 'rjKagJvksJdyUKfyPfjY'; /*从环境变量里取出数 ...
- Android数据存储之sharedpreferences与Content Provider
android中对数据操作包含有: file, sqlite3, Preferences, ContectResolver与ContentProvider前三种数据操作方式都只是针对本应用内数据,程序 ...
- Cocos2dx中零散知识点
cocos2dx中有三种定时器:schedule,scheduleUpdate,scheduleOnce.功能分别是 每隔几秒调用自定义函数.调用系统默认的update()函数.只调用一次自定义函数 ...