zoukankan      html  css  js  c++  java
  • BZOJ 3224: Tyvj 1728 普通平衡树 or 洛谷 P3369 【模板】普通平衡树-Splay树模板题

    3224: Tyvj 1728 普通平衡树

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 22483  Solved: 10130
    [Submit][Status][Discuss]

    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]
     
     
     
    题目就是一个splay的模板,直接贴代码,代码里写了注释。
     
    代码:
      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<bitset>
      6 #include<cassert>
      7 #include<cctype>
      8 #include<cmath>
      9 #include<cstdlib>
     10 #include<ctime>
     11 #include<deque>
     12 #include<iomanip>
     13 #include<list>
     14 #include<map>
     15 #include<queue>
     16 #include<set>
     17 #include<stack>
     18 #include<vector>
     19 using namespace std;
     20 typedef long long ll;
     21 
     22 const double PI=acos(-1.0);
     23 const double eps=1e-6;
     24 const ll mod=1e9+7;
     25 const int inf=0x3f3f3f3f;
     26 const int maxn=1e6+10;
     27 const int maxm=100+10;
     28 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
     29 
     30 /*
     31 平衡树的本质其实是二叉搜索树,所以很多操作是基于二叉搜索树的操作。
     32 
     33 splay的本质是rotate,旋转其实只是为了保证二叉搜索树的平衡性。
     34 
     35 所有的操作一定都满足二叉搜索树的性质,所有改变父子关系的操作一定要update
     36 */
     37 
     38 int ch[maxn][2],f[maxn],size[maxn],cnt[maxn],key[maxn];
     39 /*
     40 f[i]表示i的父节点,ch[i][0]表示i的左儿子,ch[i][1]表示i的右儿子,key[i]表示i的关键字(即节点i代表的那个数)
     41 cnt[i]表示i的关键字出现的次数(相当于权值),size[i]表示包括i在内的子树的大小
     42 */
     43 int sz,root;//sz为整棵树的大小,root为整棵树的根
     44 
     45 inline void clear(int x)//将当前点的各项清零(用于删除之后)
     46 {
     47     ch[x][0]=ch[x][1]=f[x]=size[x]=cnt[x]=key[x]=0;
     48 }
     49 
     50 inline bool get(int x)//判断当前点是它父节点的左儿子还是右儿子
     51 {
     52     return ch[f[x]][1]==x;
     53 }
     54 
     55 inline void update(int x)//更新当前点的size值(用于发生修改之后)
     56 {
     57     if(x){
     58         size[x]=cnt[x];//x的个数
     59         if(ch[x][0]) size[x]+=size[ch[x][0]];//加上左右儿子的size
     60         if(ch[x][1]) size[x]+=size[ch[x][1]];
     61     }
     62 }
     63 
     64 inline void rotate(int x)//伸展操作,旋转
     65 {
     66     int old=f[x],oldf=f[old],whichx=get(x);//找爸爸,爷爷,儿子  下面的以向右转为例子,反过来也一样的
     67     ch[old][whichx]=ch[x][whichx^1];f[ch[old][whichx]]=old;//x的右儿子变为x的爸爸的左儿子,x的右儿子的爸爸变为x的爸爸
     68     ch[x][whichx^1]=old;f[old]=x;//x的右儿子变为x以前的爸爸,x以前的爸爸的爸爸变为x
     69     f[x]=oldf;//x的爸爸变为x以前的爷爷
     70     if(oldf)
     71         ch[oldf][ch[oldf][1]==old]=x;//以前x的爷爷的儿子是x以前的爸爸,现在更新为x
     72     update(old);update(x);//更新
     73 }
     74 
     75 /*
     76 splay的过程中需要分类讨论,如果是三点一线的情况(x,x的父亲,x的祖父) 需要先rotate x的父亲,否则需要先rotate本身,(否则会形成单旋使平衡树失衡)
     77 */
     78 
     79 inline void splay(int x)//splay是rotate的发展,一直rotate到目标状态,如果有一个确定的目标状态,也可以传两个参数,一般是直接旋转到根
     80 {
     81     for(int fa;fa=f[x];rotate(x))
     82         if(f[fa])
     83             rotate(get(x)==get(fa)?fa:x);
     84         root=x;
     85 }
     86 
     87 inline void insert(int x)//插入操作
     88 {
     89     if(root==0){
     90         sz++;ch[sz][0]=ch[sz][1]=f[sz]=0;
     91         root=sz;size[sz]=cnt[sz]=1;key[sz]=x;return ;
     92     }
     93     int now=root,fa=0;
     94     while(1){
     95         if(x==key[now]){//如果这个数在树中已经出现了
     96             cnt[now]++;update(now);update(fa);splay(now);break;
     97         }
     98         fa=now;now=ch[now][key[now]<x];
     99         if(now==0){//如果到了最底下,还是没找到,说明要插入新的值,整棵树的大小+1,新节点的左儿子右儿子(虽然为空)父节点等各个值都一一对应,然后做一下他父亲的update(做自己的没必要),做一下splay
    100             sz++;ch[sz][0]=ch[sz][1]=0;
    101             f[sz]=fa;size[sz]=cnt[sz]=1;
    102             ch[fa][key[fa]<x]=sz;key[sz]=x;
    103             update(fa);splay(sz);break;
    104         }
    105     }
    106 }
    107 
    108 inline int find(int x)//查找x的排名
    109 {
    110     int now=root,ans=0;
    111     while(1){
    112         if(x<key[now]) now=ch[now][0];//x比当前的小,继续找左子树
    113         else{
    114             ans+=(ch[now][0]?size[ch[now][0]]:0);
    115             if(x==key[now]){
    116                 splay(now);return ans+1;
    117             }
    118             ans+=cnt[now];
    119             now=ch[now][1];
    120         }
    121     }
    122 }
    123 
    124 inline int findx(int x)//找到排名为x的点
    125 {
    126     int now=root;
    127     while(1){
    128         if(ch[now][0]&&x<=size[ch[now][0]])
    129             now=ch[now][0];
    130         else{
    131             int temp=(ch[now][0]?size[ch[now][0]]:0)+cnt[now];
    132             if(x<=temp) return key[now];
    133             x-=temp;now=ch[now][1];
    134         }
    135     }
    136 }
    137 
    138 /*
    139 因为在做insert操作之后做了一遍splay,这就意味着我们把x已经splay到根了,所以pre/next操作就很好实现了
    140 */
    141 
    142 inline int pre()//求x的前驱(其实就是求x的左子树的最右边的一个节点)
    143 {
    144     int now=ch[root][0];
    145     while(ch[now][1]) now=ch[now][1];
    146     return now;
    147 }
    148 
    149 inline int next()//求x的后继(其实就是求x的右子树的左边的一个节点)
    150 {
    151     int now=ch[root][1];
    152     while(ch[now][0]) now=ch[now][0];
    153     return now;
    154 }
    155 
    156 inline void del(int x)//删除操作
    157 {
    158     int whatever=find(x);//find一下,目的是将x旋转到根
    159     if(cnt[root]>1){cnt[root]--;update(root);return ;}//现在x为根,如果满足cnt[root]>1,即不只有一个x的话,直接-1
    160     if(!ch[root][0]&&!ch[root][1]) {clear(root);root=0;return ;}//如果root没有孩子,说明树上只有一个x,直接clear
    161     if(!ch[root][0]){//如果root只有左儿子或者只有右儿子,直接clear root,然后把唯一的儿子当成根,(f变为0,root变为唯一的儿子)
    162         int oldroot=root;root=ch[root][1];;f[root]=0;clear(oldroot);return ;
    163     }
    164     else if(!ch[root][1]){
    165         int oldroot=root;root=ch[root][0];f[root]=0;clear(oldroot);return ;
    166     }
    167     //其他的就是有两个儿子的情况 如果有两个儿子的话,首先我们要先选一个根,自然是x的前驱或后继,这里我们选择前驱,然后把前驱旋转到根节点,然后再把x原来的右子树当做它的右子树,update维护一下就行。
    168     int leftbig=pre(),oldroot=root;//找到新根,也就是x的前驱,将其旋转到根,然后将原来的x的右儿子接到新根的右子树上(此操作需要改变父子关系)实际上就是把x删除,不要忘记update新根
    169     splay(leftbig);
    170     ch[root][1]=ch[oldroot][1];
    171     f[ch[oldroot][1]]=root;
    172     clear(oldroot);
    173     update(root);
    174 }
    175 
    176 int main()
    177 {
    178     int n,opt,x;
    179     scanf("%d",&n);
    180     for(int i=1;i<=n;i++){
    181         scanf("%d%d",&opt,&x);
    182         switch(opt){
    183             case 1: insert(x); break;//插入x数
    184             case 2: del(x); break;//删除x数(若有多个相同的数,因只删除一个)
    185             case 3: printf("%d
    ",find(x)); break;//查询x数的排名(若有多个相同的数,因输出最小的排名)
    186             case 4: printf("%d
    ",findx(x)); break;//查询排名为x的数
    187             case 5: insert(x); printf("%d
    ",key[pre()]); del(x); break;// 求x的前驱(前驱定义为小于x,且最大的数)
    188             case 6: insert(x);printf("%d
    ",key[next()]); del(x); break;//求x的后继(后继定义为大于x,且最小的数)
    189         }
    190     }
    191 }

    先这样。。。

     
  • 相关阅读:
    hive与hbase整合
    待重写
    hive DML
    【知识强化】第六章 总线 6.1 总线概述
    【知识强化】第五章 中央处理器 5.1 CPU的功能和基本结构
    【知识强化】第四章 指令系统 4.3 CISC和RISC的基本概念
    【知识强化】第四章 指令系统 4.2 指令寻址方式
    【知识强化】第四章 指令系统 4.1 指令格式
    【知识强化】第三章 存储系统 3.6 高速缓冲存储器
    【知识强化】第三章 存储系统 3.5 双口RAM和多模块存储器
  • 原文地址:https://www.cnblogs.com/ZERO-/p/9606696.html
Copyright © 2011-2022 走看看