zoukankan      html  css  js  c++  java
  • bzoj3224 Tyvj 1728 普通平衡树题解--Treap

    题面:

    Description
    
    您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
    1. 插入x数
    2. 删除x数(若有多个相同的数,因只删除一个)
    3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
    4. 查询排名为x的数
    5. 求x的前驱(前驱定义为小于x,且最大的数)
    6. 求x的后继(后继定义为大于x,且最小的数)
    
    Input
    
    第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
    
    Output
    
    对于操作3,4,5,6每行输出一个数,表示对应答案
    
    Sample Input
    
    10
    
    1 106465
    
    4 1
    
    1 317721
    
    1 460929
    
    1 644985
    
    1 84185
    
    1 89851
    
    6 81968
    
    1 492737
    
    5 493598
    
    Sample Output
    
    106465
    
    84185
    
    492737
    
    HINT
    
    1.n的数据范围:n<=100000
    
    2.每个数的数据范围:[-2e9,2e9]
    View Code

    题解:

      今天第一次接触平衡二叉树的概念,做了一道模版题,觉得Treap这东西很神奇啊~

    顾名思义,Treap=heap+tree,就是把堆和二叉树结合在了一起。

    但是为什么不用一般的二叉树呢?(为了给我们增大代码量)不对,是因为普通树原来是log(n)级别的,但是经过各种insert啊,del啊什么的就可能失去“平衡”,节点集中在一侧什么的,结果成了一条长链,这就把log(n)的算法变成O(n)级的线性了。

    而现在的Treap就是解决这个问题的方法之一。

    Treap中的节点在满足树的性质(左儿子都小,右儿子都大之类的)的同时,还对每个节点加入了一个“优先级”,并将节点按照堆的性质排序。这里的优先级采用随机生成的方式,所以节点的左右分布是随机的,以此保证整棵树的相对平衡。

    那我们怎么样对这些节点进行堆的排序呢?

    这就有一个看起来很厉害的操作了--旋转

    旋转分为左旋和右旋。当我们某个节点的左儿子优先度大于本节点,就需要进行右旋,右儿子大,就左旋。

    二叉左旋
    一棵二叉平衡树的子树,根是Root,左子树是x,右子树的根为RootR,右子树的两个孩子树分别为RLeftChild和RRightChild。则左旋后,该子树的根为RootR,右子树为RRightChild,左子树的根为Root,Root的两个孩子树分别为x(左)和RLeftChild(右)。
    二叉右旋
    一棵二叉平衡树的子树,根是Root,右子树是x,左子树的根为RootL,左子树的两个孩子树分别为LLeftChild和LRightChild。则右旋后,该子树的根为RootL,左子树为LLeftChild,右子树的根为Root,Root的两个孩子树分别为LRightChild(左)和x(右)。

    来自百度百科,个人觉得挺容易懂的。

    那么问题来了,为什么你这么一转,还能保持树的性质成立呢?为什么不会把节点权小的和大的弄返呢?不会把树弄乱吗?

    这就是旋转的真正厉害之处了----可以发现,旋转前后,该子树的中序遍历是不变的!就是说并不会改变数列的大小顺序。我也不是很懂具体是为什么能这样,但是确实很厉害。

    剩下就没什么了,树嘛,插入删除的都比较基础。

    放代码:

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 const int maxn=100010,inf=100000000;
      4 int cnt,ret,n,t1,t2,root;
      5 struct treap{
      6     int lc,rc,key,pri,siz,val;
      7 /*key是关键字(权),pri是优先度,siz为子树大小,val表示key这个数有几个*/
      8 }a[maxn];
      9 void pushup(int &o){
     10     a[o].siz=a[a[o].lc].siz+a[a[o].rc].siz+a[o].val;
     11     return;
     12 }
     13 void lturn(int &o){
     14     int t=a[o].rc;
     15     a[o].rc=a[t].lc;
     16     a[t].lc=o;
     17     a[t].siz=a[o].siz;
     18     pushup(o);
     19     o=t;
     20     return;
     21 }
     22 void rturn(int &o){
     23     int t=a[o].lc;
     24     a[o].lc=a[t].rc;
     25     a[t].rc=o;
     26     a[t].siz=a[o].siz;
     27     pushup(o);
     28     o=t;
     29     return;
     30 }
     31 void insert(int &o,int t){
     32     if(!o){
     33         o=++cnt;
     34         a[o]=(treap){0,0,t,rand(),1,1};//rand()随机一个优先度
     35         return;
     36     }
     37     a[o].siz++;
     38     if(t==a[o].key)a[o].val++;
     39     else if(t<a[o].key){
     40         insert(a[o].lc,t);
     41         if(a[a[o].lc].pri>a[o].pri)rturn(o);
     42     }
     43     else{
     44         insert(a[o].rc,t);
     45         if(a[a[o].rc].pri>a[o].pri)lturn(o);//
     46     }
     47     return;
     48 }
     49 void del(int &o,int k){
     50     if(!o)return;
     51     if(k==a[o].key){
     52         if(a[o].val>1){
     53             a[o].val--;
     54             a[o].siz--;
     55         }
     56         else if(!(a[o].lc*a[o].rc)){//如果左右只有一个儿子
     57             o=a[o].lc+a[o].rc;
     58         }
     59         else if(a[a[o].lc].pri<a[a[o].rc].pri){
     60             lturn(o);
     61             del(o,k);
     62         }
     63         else{
     64             rturn(o);
     65             del(o,k);
     66         }
     67     }
     68     else if(k<a[o].key)
     69     {
     70         --a[o].siz;
     71         del(a[o].lc,k);
     72     }
     73     else
     74     {
     75         --a[o].siz;
     76         del(a[o].rc,k);
     77     }
     78     return;
     79 }
     80 int query_rank(int o,int k){
     81     if(!o)return 0;
     82     if(k<a[o].key)return query_rank(a[o].lc,k);
     83     if(k==a[o].key)return a[a[o].lc].siz+1;
     84     return a[a[o].lc].siz+a[o].val+query_rank(a[o].rc,k);
     85 }
     86 int query_num(int o,int k){
     87     if(!o)return 0;
     88     if(k<=a[a[o].lc].siz)return query_num(a[o].lc,k);
     89     if(k<=a[a[o].lc].siz+a[o].val)return a[o].key;
     90     return query_num(a[o].rc,k-a[a[o].lc].siz-a[o].val);
     91 }
     92 void query_pre(int o,int k){
     93     if(!o)return;
     94     if(k<=a[o].key)query_pre(a[o].lc,k);
     95     else{
     96         ret=a[o].key;
     97         query_pre(a[o].rc,k);
     98     }
     99     return;
    100 }
    101 void query_pos(int o,int k){
    102     if(!o)return;
    103     if(k>=a[o].key)query_pos(a[o].rc,k);
    104     else{
    105         ret=a[o].key;
    106         query_pos(a[o].lc,k);
    107     }
    108     return;
    109 }
    110 int main(){
    111     scanf("%d",&n);
    112     srand(n);
    113     for(int i=1;i<=n;i++){
    114         scanf("%d%d",&t1,&t2);
    115         if(t1==1)insert(root,t2);
    116         else if(t1==2)del(root,t2);
    117         else if(t1==3)printf("%d
    ",query_rank(root,t2));
    118         else if(t1==4)printf("%d
    ",query_num(root,t2));
    119         else if(t1==5){
    120             ret=-inf;
    121             query_pre(root,t2);
    122             printf("%d
    ",ret);
    123         }
    124         else if(t1==6){
    125             ret=inf;
    126             query_pos(root,t2);
    127             printf("%d
    ",ret);
    128         }
    129     }
    130     return 0;
    131 }
  • 相关阅读:
    (转)Java线程:线程的同步与锁
    (转)线程栈模型与线程的变量、线程状态转换
    (转)概念与原理
    Oracle 技术支持之现场优化的思维路径
    oracle复合索引的选择和使用
    oracle中sql执行性能关注点
    oracle中位图索引和B-tree索引的区别
    oracle锁表
    oracle索引的理解
    oracle海量数据中提升创建索引的速度
  • 原文地址:https://www.cnblogs.com/Requiescat/p/7545898.html
Copyright © 2011-2022 走看看