zoukankan      html  css  js  c++  java
  • 普通平衡树——Scapegoat _ Tree(替罪羊树)/splay(伸展树)

    学习自:https://www.luogu.org/blog/user28084/solution-p3369

    简述:看了一个下午+晚上搞出来的东西。

    题目:

      您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

    1. 插入 x 数;

    2. 删除 x 数(若有多个相同的数,因只删除一个);

    3. 查询 x 数的排名(若有多个相同的数,因输出最小的排名);

    4. 查询排名为 x 的数;

    5. 求 x 的前趋(前趋定义为小于 x ,且最大的数);

    6. 求 x 的后继(后继定义为大于 x ,且最小的数)。

    解题报告:

    代码已注释。

    1.替罪羊树

      1 /*替罪羊树
      2 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
      3 1.插入 x 数;
      4 2.删除 x 数(若有多个相同的数,因只删除一个);
      5 3.查询 x 数的排名(若有多个相同的数,因输出最小的排名);
      6 4.查询排名为 x 的数;
      7 5.求 x 的前趋(前趋定义为小于 x ,且最大的数);
      8 6.求 x 的后继(后继定义为大于 x ,且最小的数)。*/
      9 #include<bits/stdc++.h>
     10 #define numm ch-48
     11 #define pd putchar(' ')
     12 #define pn putchar('
    ')
     13 #define pb push_back
     14 #define debug(args...) cout<<#args<<"->"<<args<<endl
     15 #define bug cout<<"************"
     16 using namespace std;
     17 template <typename T>
     18 void read(T &res) {
     19     bool flag=false;char ch;
     20     while(!isdigit(ch=getchar())) (ch=='-')&&(flag=true);
     21     for(res=numm;isdigit(ch=getchar());res=(res<<1)+(res<<3)+numm);
     22     flag&&(res=-res);
     23 }
     24 template <typename T>
     25 void write(T x) {
     26     if(x<0) putchar('-'),x=-x;
     27     if(x>9) write(x/10);
     28     putchar(x%10+'0');
     29 }
     30 typedef long long ll;
     31 typedef long double ld;
     32 const int maxn=2e5+10;
     33 const ll mod=1e9+7;
     34 const int inf=0x3f3f3f3f;
     35 const double alpha=0.7;
     36 #define il inline
     37 struct node {
     38     int exist,son[2],sze,valid,val;
     39     ///valid:当前子树的真实大小
     40     ///sze:当前子树的虚假大小
     41     ///exist:当前节点是否存在
     42     ///val:当前节点的权值
     43     ///son[0]:左儿子,son[1]:右儿子
     44 }e[maxn];
     45 int memory[maxn],cur[maxn],mpos,cpos,to_rebuild,root;
     46 ///memory[]:内存池(下标mpos);cur[]:存放要重建的树的中序遍历(下标cpos)
     47 il bool isbad(int now) {   ///以当前节点为根的子树是否需要重建
     48     if((double)e[now].valid*alpha<=(double)max(e[e[now].son[0]].valid,e[e[now].son[1]].valid))
     49         return true;    ///需要重建
     50     else return false;
     51 }
     52 il void build(int l,int r,int &now) {///对有序序列进行重构二叉树
     53     int mid=l+r>>1;
     54     now=cur[mid];   ///每次都相当于把中间这个数提出来
     55     if(l==r) {
     56         e[now].son[0]=e[now].son[1]=0;
     57         e[now].valid=e[now].sze=1;
     58         return ;
     59     }
     60     if(l<mid) build(l,mid-1,e[now].son[0]); ///mid这个结点已经建完了
     61     else e[now].son[0]=0;
     62     build(mid+1,r,e[now].son[1]);///这里为什么不用判呢?想一下l=3,r=4的情况
     63     e[now].sze=e[e[now].son[0]].sze+e[e[now].son[1]].sze+1; ///+1要划重点!!!
     64     e[now].valid=e[e[now].son[0]].valid+e[e[now].son[1]].valid+1;///+1就是加本身
     65     return ;
     66 }
     67 il void dfs(int now) { ///求中序遍历
     68     if(!now) return ;
     69     dfs(e[now].son[0]); ///中序遍历先遍历左子树
     70     if(e[now].exist) cur[++cpos]=now;///存在的结点压入cur
     71     else memory[++mpos]=now;///不存在的则回收
     72     dfs(e[now].son[1]);
     73     return ;
     74 }
     75 il void rebuild(int &now) {///重建二叉树
     76     cpos=0;     ///attention!!!
     77     dfs(now);
     78     if(cpos) build(1,cpos,now);
     79     else now=0; ///没有要重建的,now=0
     80 }
     81 il void ins(int &now,int val) {    ///插入一个数
     82     if(!now) {
     83         now=memory[mpos--]; ///分配一个空间给当前结点,作为它的下标
     84         e[now].val=val;
     85         e[now].exist=e[now].valid=e[now].sze=1;
     86         e[now].son[0]=e[now].son[1]=0;
     87         return ;
     88     }
     89     e[now].sze++,e[now].valid++;
     90     if(val<=e[now].val) ins(e[now].son[0],val);///比当前的值大往右,否则往左
     91     else ins(e[now].son[1],val);
     92     if(!isbad(now)) {   ///如果回溯到当前节点不用重建,就判断在它之前回溯的是否有需要重建的
     93         if(to_rebuild) {
     94             if(to_rebuild==e[now].son[0]) rebuild(e[now].son[0]);///判断左右子树哪个需要重建
     95             else rebuild(e[now].son[1]);
     96             to_rebuild=0;
     97         }
     98     }
     99     else to_rebuild=now;///当前节点需要重建,先不重建,继续回溯
    100 }
    101 il int find_rnk(int val) {///查找权值为val对应的排名
    102     int ans=1,now=root;
    103     while(now) {
    104         if(e[now].val>=val) now=e[now].son[0];
    105         else {  ///下面两句话不要写颠倒!!!
    106             ans+=e[e[now].son[0]].valid+e[now].exist;
    107             now=e[now].son[1];
    108         }
    109     }
    110     return ans;
    111 }
    112 il int find_val(int k) {   ///查找排名为k对应的权值
    113     int now=root;
    114     while(now) {
    115         if(e[now].exist&&e[e[now].son[0]].valid+1==k) return e[now].val;
    116         if(e[e[now].son[0]].valid>=k) now=e[now].son[0];
    117         else {  ///下面两句话不要写颠倒!!!
    118             k=k-e[e[now].son[0]].valid-e[now].exist;
    119             now=e[now].son[1];
    120         }
    121     }
    122 }
    123 il void del_rnk(int &now,int k) {  ///删除排名为k的结点
    124     if(e[now].exist&&e[e[now].son[0]].valid+1==k) {///当前的now为要找的结点
    125         e[now].exist=0;
    126         e[now].valid--;
    127         return ;
    128     }
    129     e[now].valid--;
    130     if(e[e[now].son[0]].valid+e[now].exist>=k) del_rnk(e[now].son[0],k);
    131     else del_rnk(e[now].son[1],k-e[e[now].son[0]].valid-e[now].exist);
    132     return ;
    133 }
    134 il void del_val(int val) { ///删除权值为val的数(删除靠左的第一个数)
    135     del_rnk(root,find_rnk(val));
    136     if((double)e[root].sze*alpha>(double)e[root].valid) rebuild(root);  ///如果删除的结点太多,则也需要重建
    137 }
    138 
    139 int main()
    140 {
    141     int n,op,val;
    142     for(int i=1;i<=maxn-1;i++)
    143         memory[i]=i;
    144     while(scanf("%d",&n)!=EOF) {
    145         root=to_rebuild=0;
    146         e[0].valid=0;
    147         mpos=(int)2e5-1;   ///内存池下标初始化
    148         for(int i=1;i<=n;i++) {
    149             read(op);read(val);
    150             if(op==1)
    151                 ins(root,val);
    152             else if(op==2)
    153                 del_val(val);
    154             else if(op==3)
    155                 write(find_rnk(val)),pn;
    156             else if(op==4)
    157                 write(find_val(val)),pn;
    158             else if(op==5)
    159                 write(find_val(find_rnk(val)-1)),pn;
    160                 ///find_rnk总会找到val最小的rnk,-1就是最后一个比它小的数的rnk
    161             else if(op==6)
    162                 write(find_val(find_rnk(val+1))),pn;
    163                 ///找到比val小(包括val)的数的数量,直接查找该rnk对应的val
    164         }
    165         pn;
    166     }
    167     return 0;
    168 }
    View Code

    2.splay

      1 /*伸展树
      2 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
      3 1.插入 x 数;
      4 2.删除 x 数(若有多个相同的数,因只删除一个);
      5 3.查询 x 数的排名(若有多个相同的数,因输出最小的排名);
      6 4.查询排名为 x 的数;
      7 5.求 x 的前趋(前趋定义为小于 x ,且最大的数);
      8 6.求 x 的后继(后继定义为大于 x ,且最小的数)。*/
      9 #include<bits/stdc++.h>
     10 #define numm ch-48
     11 #define pd putchar(' ')
     12 #define pn putchar('
    ')
     13 using namespace std;
     14 template <typename T>
     15 void read(T &res) {
     16     bool flag=false;char ch;
     17     while(!isdigit(ch=getchar())) (ch=='-')&&(flag=true);
     18     for(res=numm;isdigit(ch=getchar());res=(res<<1)+(res<<3)+numm);
     19     flag&&(res=-res);
     20 }
     21 template <typename T>
     22 void write(T x) {
     23     if(x<0) putchar('-'),x=-x;
     24     if(x>9) write(x/10);
     25     putchar(x%10+'0');
     26 }
     27 const int maxn=1e6+10;
     28 #define il inline
     29 int cnt[maxn],sze[maxn],ch[maxn][2],key[maxn],f[maxn],sz,root;
     30 ///key[x]:结点x原本的大小
     31 ///cnt[x]:当前结点存的权值(key[x]的个数)
     32 ///sze[x]:当前结点x的子树(包含x)大小
     33 ///ch[x][0]:左儿子、ch[x][1]:右儿子
     34 ///f[x]:x的父亲
     35 ///sz:整棵树的大小
     36 il void init(int n) {///n为操作总数
     37     sz=root=0;
     38     for(int i=0;i<=n;i++)
     39         ch[i][0]=ch[i][1]=f[i]=sze[i]=key[i]=cnt[i]=0;
     40 }
     41 il void clearly(int x) {///用于删除之后
     42     cnt[x]=sze[x]=ch[x][0]=ch[x][1]=f[x]=key[x]=0;
     43 }
     44 il void update(int x) {///更新当前结点的子树大小
     45     if(x) {
     46         sze[x]=cnt[x];
     47         sze[x]+=ch[x][0]?sze[ch[x][0]]:0;
     48         sze[x]+=ch[x][1]?sze[ch[x][1]]:0;
     49     }
     50 }
     51 il int getf(int x) {///判断结点x是他父亲的左儿子还是右儿子
     52     return ch[f[x]][1]==x;
     53 }
     54 il void rotated(int x) {///将x旋转到其父节点
     55     int old=f[x],oldf=f[old],which=getf(x);
     56     ch[old][which]=ch[x][which^1];f[ch[old][which]]=old;
     57     f[old]=x;ch[x][which^1]=old;
     58     f[x]=oldf;
     59     if(oldf)   ///如果爷爷结点存在的话,要连接x
     60         ch[oldf][ch[oldf][1]==old]=x;
     61     update(old);update(x);///先update下面的结点:old,然后是x
     62 }
     63 il void splay(int x) {///将x一直rotate到根
     64     for(int fa;fa=f[x];rotated(x))
     65         if(f[fa])///如果三点一线要先rotatex的父亲
     66             rotated((getf(fa)==getf(x))?fa:x);
     67     root=x;///attention!!!
     68 }
     69 il void inserted(int x) {
     70     if(!root) {
     71         sz++;
     72         f[sz]=ch[sz][0]=ch[sz][1]=0;
     73         cnt[sz]=sze[sz]=1;
     74         key[sz]=x;
     75         root=sz;    ///attention!!!
     76         return ;
     77     }
     78     int now=root,fa=0;
     79     while(1) {
     80         if(key[now]==x) {
     81             cnt[now]++;
     82             update(now);
     83             splay(now);
     84             break;
     85         }
     86         fa=now;
     87         now=ch[now][key[now]<x];
     88         if(!now) {
     89             sz++;
     90             ch[sz][0]=ch[sz][1]=0;
     91             cnt[sz]=sze[sz]=1;
     92             key[sz]=x;
     93             f[sz]=fa;
     94             ch[fa][key[fa]<x]=sz;
     95             update(fa);
     96             splay(sz);
     97             break;
     98         }
     99     }
    100 }
    101 il int x_find_pos(int x) {
    102     int ans=0,now=root;
    103     while(true) {
    104         if(x<key[now])
    105             now=ch[now][0];
    106         else {
    107             ans+=(ch[now][0]?sze[ch[now][0]]:0);
    108             if(x==key[now]) {
    109                 splay(now);
    110                 return ans+1;
    111             }
    112             ans+=cnt[now];
    113             now=ch[now][1];
    114         }
    115     }
    116 }
    117 il int pos_find_x(int pos) {
    118     int now=root;
    119     while(true) {
    120         if(ch[now][0]&&pos<=sze[ch[now][0]])
    121             now=ch[now][0];
    122         else {
    123             int temp=(ch[now][0]?sze[ch[now][0]]:0)+cnt[now];
    124             if(pos<=temp)
    125                 return key[now];
    126             pos-=temp;
    127             now=ch[now][1];
    128         }
    129     }
    130 }
    131 il int pre() {
    132     int now=ch[root][0];
    133     while(ch[now][1]) now=ch[now][1];
    134     return now;
    135 }
    136 il int net() {
    137     int now=ch[root][1];
    138     while(ch[now][0]) now=ch[now][0];
    139     return now;
    140 }
    141 il void del(int x) {///删除一个权值为x的数
    142     x_find_pos(x);///目的是把x提到树根
    143     if(cnt[root]>1) {
    144         cnt[root]--;
    145         update(root);
    146         return ;
    147     }
    148     if(!ch[root][0]&&!ch[root][1]) {
    149         clearly(root);
    150         root=0;///attention!!!
    151         return;///只有一个点的情况
    152     }
    153     if(!ch[root][0]) {
    154         int oldroot=root;
    155         root=ch[root][1];
    156         f[root]=0;
    157         clearly(oldroot);
    158         return ;
    159     }
    160     if(!ch[root][1]) {
    161         int oldroot=root;
    162         root=ch[root][0];
    163         f[root]=0;
    164         clearly(oldroot);
    165         return ;
    166     }
    167     int leftbig=pre();///找到前驱所在位置
    168     int oldroot=root;
    169     splay(leftbig);///把前驱提到树根,目的:使根结点成为他的左儿子
    170     f[ch[oldroot][1]]=root;
    171     ch[root][1]=ch[oldroot][1];
    172     clearly(oldroot);
    173     update(root);///attention!!!
    174     return ;
    175 }
    176 int main()
    177 {
    178     int n;
    179     while(scanf("%d",&n)!=EOF) {
    180         int op,x;
    181         init(n);
    182         for(int i=1;i<=n;i++) {
    183             read(op),read(x);
    184             if(op==1) inserted(x);
    185             else if(op==2) del(x);
    186             else if(op==3) write(x_find_pos(x)),pn;
    187             else if(op==4) write(pos_find_x(x)),pn;
    188             else if(op==5) inserted(x),write(key[pre()]),pn,del(x);
    189             else if(op==6) inserted(x),write(key[net()]),pn,del(x);
    190         }
    191         pn;
    192     }
    193     return 0;
    194 }
    View Code
  • 相关阅读:
    Pulp之四:其它应用样例(1)-一般的整数规划问题 (设置目标约束函数)
    Pulp之三:官网上的应用样例(4)-Blending Problem (混合问题)
    Pulp之三:官网上的应用样例(3)-Sudoku Problem by LP (数独问题)
    Pulp之三:官网上的应用样例(2)-A Set Partitioning Problem (集合划分问题)
    list转换dict的方式以及zip的用法
    Pulp之三:官网上的应用样例(1)-The_Whiskas_Problem (猫粮配料比例问题)
    Pulp之二:Pulp中几个重要的概念
    一个简单有趣的题(4个变量找出取走的数)
    Java之父 James Gosling 发表博文 《Too Soon》纪念乔布斯。
    第01课 OpenGL窗口(1)
  • 原文地址:https://www.cnblogs.com/wuliking/p/11768962.html
Copyright © 2011-2022 走看看