华师一附中OI组

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1062|回复: 2
打印 上一主题 下一主题

P3369 【模板】普通平衡树

[复制链接]

738

主题

1485

帖子

5422

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5422
跳转到指定楼层
楼主
发表于 2020-3-22 18:54:33 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
https://www.luogu.com.cn/problem/P3369


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

插入 x 数
删除 x 数(若有多个相同的数,因只删除一个)
查询 x 数的排名(排名定义为比当前数小的数的个数 +1 )
查询排名为 x 的数
求 x 的前驱(前驱定义为小于 x,且最大的数)
求 x 的后继(后继定义为大于 x,且最小的数)
输入格式
第一行为 n,表示操作的个数,下面 n 行每行有两个数opt 和 xx,opt 表示操作的序号(1≤opt≤6 )

输出格式
对于操作 3,4,5,6 每行输出一个数,表示对应答案

输入输出样例
输入 #1复制
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
输出 #1复制
106465
84185
492737
说明/提示
【数据范围】
对于 100% 的数据,1≤n≤10^5∣x∣≤10^7。
回复

使用道具 举报

738

主题

1485

帖子

5422

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5422
沙发
 楼主| 发表于 2020-3-22 18:55:36 | 只看该作者
训练简单平衡树的好题,可以用多种树结构做,我先做了有个替罪羊树:
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1001005;
  4. const float p=0.75;
  5. struct node
  6. {
  7.         int val,cnt;
  8.         int ls,rs,sz;  ///lsrs左右儿子  sz本子树下所有节点个数
  9. } a[N];
  10. int ta[N],ita;///转移数组

  11. int n, tot, rt;
  12. void ArrayPr()
  13. {
  14.         for (int i=1; i<=tot; i++) cout<<setw(3)<<i;
  15.         cout<<endl;
  16.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].val;
  17.         cout<<endl;
  18.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].cnt;
  19.         cout<<endl;
  20.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].ls;
  21.         cout<<endl;
  22.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].rs;
  23.         cout<<endl;
  24.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].sz;
  25.         cout<<endl;

  26. }

  27. void BSTPr(int rt) ///中序输出以rt为根的BST
  28. {
  29.         if (a[rt].ls>0)  BSTPr(a[rt].ls);
  30.         cout<<a[rt].val<<' '; /// 也可以加上cnt
  31.         if (a[rt].rs>0)  BSTPr(a[rt].rs);
  32. }

  33. void pushup(int rt)
  34. {
  35.         int l=a[rt].ls, r=a[rt].rs;
  36.         a[rt].sz=a[l].sz+a[r].sz+a[rt].cnt;
  37. }

  38. int build(int l, int r)//l-r重新建树
  39. {
  40.         if(l>r) return 0;
  41.         int m=(l+r)/2; ///根结点是最中间的那个
  42.         a[ta[m]].ls=build(l,m-1);
  43.         a[ta[m]].rs=build(m+1,r);
  44.         pushup(ta[m]);
  45.         return ta[m];
  46. }

  47. void Inorder(int rt)//倒到数组
  48. {
  49.         if(rt>0)
  50.         {
  51.                 Inorder(a[rt].ls);
  52.                 ta[++ita]=rt;
  53.                 Inorder(a[rt].rs);
  54.         }
  55. }

  56. void RB(int &rt)  ///重建rt
  57. {
  58.         if(rt==0) return;
  59.         float t=a[rt].sz*p; ///左右儿子不能大于rt的0,75
  60.         bool b1=a[a[rt].ls].sz>t;
  61.         bool b2=a[a[rt].rs].sz>t;

  62.         if( b1|| b2)
  63.         {
  64.                 ita=0;
  65.                 Inorder(rt);
  66.                 rt=build(1,ita);
  67.         }
  68. }

  69. void Ins(int &rt, int x)
  70. {
  71.         if(rt==0)
  72.         {
  73.                 rt=++tot;
  74.                 a[rt].val=x;
  75.                 a[rt].sz=a[rt].cnt = 1;
  76.         }
  77.         else
  78.         {
  79.                 RB(rt); /// 比原版多一句调整
  80.                 if(a[rt].val==x) a[rt].cnt ++,a[rt].sz++;
  81.                 else
  82.                 {
  83.                         if(a[rt].val<x) Ins(a[rt].rs, x);
  84.                         else if(a[rt].val>x) Ins(a[rt].ls, x);
  85.                         pushup(rt); /// 比原版多一句调整  线段树相似
  86.                 }
  87.         }
  88. }

  89. int DelMin(int &rt)  ///原版不变
  90. {
  91.         int t;
  92.         if(a[rt].ls==0)///没有左儿子 那就是它
  93.         {
  94.                 t=rt;
  95.                 rt=a[rt].rs;
  96.                 return t;
  97.         }
  98.         else
  99.         {
  100.                 t=DelMin(a[rt].ls); ///递归
  101.                 pushup(rt);///多了这个调整
  102.                 return t;
  103.         }

  104. }

  105. void Del(int &rt,int x)
  106. {
  107.         if (rt==0) return ;
  108.         else if(a[rt].val>x)    ///左儿子中去找
  109.         {
  110.                 Del(a[rt].ls,x);
  111.                 pushup(rt);
  112.         }
  113.         else if(a[rt].val<x)   ///右儿子中去找
  114.         {
  115.                 Del(a[rt].rs,x);
  116.                 pushup(rt);
  117.         }
  118.         else if(a[rt].val==x)
  119.         {
  120.                 if(a[rt].cnt>1)a[rt].cnt--,a[rt].sz--;  ///次数>1
  121.                 else if(a[rt].ls==0)rt=a[rt].rs;
  122.                 else if(a[rt].rs==0)rt=a[rt].ls;
  123.                 else
  124.                 {
  125.                         int t=DelMin(a[rt].rs);
  126.                         a[rt].val=a[t].val;
  127.                         a[rt].cnt=a[t].cnt;
  128.                         pushup(rt);
  129.                 }
  130.         }
  131. }

  132. int Rank(int rt, int x) ///在RT子树中比x小的数 +1
  133. {
  134.         if(a[rt].val==x) return a[a[rt].ls].sz+1; /// 找到了
  135.         if(a[rt].val<x) return a[a[rt].ls].sz +a[rt].cnt+ Rank(a[rt].rs,x);///左子树下面的都算
  136.         if(a[rt].val>x) return Rank(a[rt].ls,x);
  137. }

  138. int Getkth(int rt, int x) ///在RT子树中找第k小
  139. {
  140.         int l=a[a[rt].ls].sz;  /// RT的左儿子下的个数
  141.         if(l+1<=x && x<=l+a[rt].cnt) return a[rt].val;
  142.         if(l+1>x) return Getkth(a[rt].ls, x); ///左儿子里去找
  143.         if(l+a[rt].cnt<x) return Getkth(a[rt].rs, x-(l+a[rt].cnt));
  144. }

  145. int Pre(int rt, int x)
  146. {
  147.         int p=rt, ans;
  148.         while(p>0)
  149.         {
  150.                 if(x<=a[p].val) p=a[p].ls;
  151.                 else
  152.                 {
  153.                         ans=p;
  154.                         p=a[p].rs;
  155.                 }
  156.         }
  157.         return ans;
  158. }
  159. int Suc(int rt, int x)
  160. {
  161.         int p=rt, ans;
  162.         while(p>0)
  163.         {
  164.                 if(a[p].val<=x) p=a[p].rs;
  165.                 else
  166.                 {
  167.                         ans = p;
  168.                         p=a[p].ls;
  169.                 }
  170.         }
  171.         return ans;
  172. }

  173. int main()
  174. {
  175.         cin>>n;
  176.         while(n--)
  177.         {
  178.                 int opt, x;
  179.                 cin>>opt>>x;
  180.                 if(opt == 1) Ins(rt, x);
  181.                 if(opt == 2) Del(rt, x);
  182.                 if(opt == 3) cout<<Rank(rt, x)<<endl;
  183.                 if(opt == 4) cout<<Getkth(rt, x)<<endl;
  184.                 if(opt == 5) cout<<a[Pre(rt, x)].val<<endl;
  185.                 if(opt == 6) cout<<a[Suc(rt, x)].val<<endl;
  186.                 ///ArrayPr();
  187.                 ///BSTPr(rt);
  188.         }
  189.         return 0;
  190. }
复制代码
回复 支持 反对

使用道具 举报

738

主题

1485

帖子

5422

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5422
板凳
 楼主| 发表于 2020-3-29 10:37:53 | 只看该作者
再来一个 treap

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int inf=9999999;
  4. const int N=100100;
  5. struct node
  6. {
  7.         int val,cnt;
  8.         int ls,rs,sz,rd;  ///lsrs左右儿子  sz本子树下所有节点个数  rd随机值
  9. } a[N];
  10. int n, tot, rt;
  11. void ArrayPr()   /// 调试监视使用
  12. {
  13.         cout<<endl<<"  i ";
  14.         for (int i=1; i<=tot; i++) cout<<setw(3)<<i;
  15.         cout<<endl<<"val ";
  16.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].val;
  17.         cout<<endl<<"cnt ";
  18.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].cnt;
  19.         cout<<endl<<" ls ";
  20.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].ls;
  21.         cout<<endl<<" rs ";
  22.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].rs;
  23.         cout<<endl<<" sz ";
  24.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].sz;
  25.         cout<<endl<<" rd ";
  26.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].rd;
  27.         cout<<endl;
  28. }

  29. void BSTPr(int rt)   ///中序输出以rt为根的BST
  30. {
  31.         if (a[rt].ls>0)  BSTPr(a[rt].ls);
  32.         cout<<a[rt].val<<' '; /// 也可以加上cnt
  33.         if (a[rt].rs>0)  BSTPr(a[rt].rs);
  34. }

  35. void pushup(int rt)   ///类似线段树网上调整
  36. {
  37.         int l=a[rt].ls, r=a[rt].rs;
  38.         a[rt].sz=a[l].sz+a[r].sz+a[rt].cnt;
  39. }

  40. void RR(int &rt) /// 把x的左儿子旋上来当根
  41. {
  42.         int l=a[rt].ls;
  43.         a[rt].ls=a[l].rs;///左儿子的右儿子变成了我的左儿子
  44.         a[l].rs=rt;///自己变成了左儿子的右儿子
  45.         pushup(rt),        pushup(l),rt=l;///挂上
  46. }
  47. void LR(int &rt)/// 把x的右儿子旋上来当根
  48. {
  49.         int r=a[rt].rs;
  50.         a[rt].rs=a[r].ls;///右儿子的左儿子变成了我的右儿子
  51.         a[r].ls=rt;///自己变成了右儿子的左儿子
  52.         pushup(rt),pushup(r),rt=r;///挂上
  53. }


  54. void Ins(int &rt, int x)   /// 在rt字数中插入x
  55. {
  56.         if(rt==0)   ///空树就新建
  57.                 {
  58.                         rt=++tot;
  59.                         a[rt].val=x;
  60.                         a[rt].sz=a[rt].cnt=1;
  61.                         a[rt].rd=rand();
  62.                 }
  63.         else
  64.                 {
  65.                         a[rt].sz++;
  66.                         if(a[rt].val==x) a[rt].cnt++;
  67.                         else
  68.                                 {
  69.                                         if(a[rt].val<x)
  70.                                                 {
  71.                                                         ///cout<<'R';
  72.                                                         Ins(a[rt].rs,x);
  73.                                                         if(a[rt].rd>a[a[rt].rs].rd) LR(rt);
  74.                                                 }
  75.                                         else if(a[rt].val>x)
  76.                                                 {
  77.                                                         ///cout<<'L';
  78.                                                         Ins(a[rt].ls, x);
  79.                                                         if(a[rt].rd>a[a[rt].ls].rd) RR(rt);
  80.                                                 }

  81.                                 }
  82.                 }
  83. }

  84. void Del(int &rt,int x)
  85. {
  86.         if(rt>0)
  87.                 if(a[rt].val==x)
  88.                         {
  89.                                 if(a[rt].cnt>1)        a[rt].cnt--, a[rt].sz--;    //有相同的就直接cnt--
  90.                                 else
  91.                                         {
  92.                                                 if (a[rt].ls==0) rt=a[rt].rs;
  93.                                                 else if (a[rt].rs==0) rt=a[rt].ls;
  94.                                                 else
  95.                                                         {
  96.                                                                 if (a[a[rt].ls].rd>a[a[rt].rs].rd) RR(rt);
  97.                                                                 else LR(rt);
  98.                                                                 Del(rt,x);
  99.                                                         }
  100.                                         }
  101.                         }
  102.                 else
  103.                         {
  104.                                 a[rt].sz--;
  105.                                 if (a[rt].val<x) Del(a[rt].rs, x);
  106.                                 else Del(a[rt].ls, x);
  107.                         }
  108. }

  109. int Rank(int rt, int x)   ///在RT子树中比x小的数 +1
  110. {
  111.         if(a[rt].val==x) return a[a[rt].ls].sz+1; /// 找到了
  112.         if(a[rt].val<x) return a[a[rt].ls].sz +a[rt].cnt+ Rank(a[rt].rs,x);///左子树下面的都算
  113.         if(a[rt].val>x) return Rank(a[rt].ls,x);
  114. }

  115. int Getkth(int rt, int k)   ///在RT子树中找第k小
  116. {
  117.         int l=a[a[rt].ls].sz;  /// RT的左儿子下的个数
  118.         if(l+1<=k && k<=l+a[rt].cnt) return a[rt].val;
  119.         if(l+1>k) return Getkth(a[rt].ls, k); ///左儿子里去找
  120.         if(l+a[rt].cnt<k) return Getkth(a[rt].rs, k-(l+a[rt].cnt));///右儿子里去找
  121. }

  122. int Pre(int rt, int x)   ///小于x的最大数 这里好像有问题
  123. {
  124.         if(rt==0) return 0;
  125.         if(a[rt].val>=x) return Pre(a[rt].ls,x);//递归查找左子树
  126.         else
  127.                 {
  128.                         int t=Pre(a[rt].rs,x);
  129.                         if (a[t].val<a[rt].val) return rt;//找右子树中是否存在前驱
  130.                         else return t;
  131.                 }
  132. }


  133. int Suc(int rt, int x)  ///大于x的最小
  134. {
  135.         int p=rt, ans;
  136.         while(p>0)
  137.                 {
  138.                         if(a[p].val<=x) p=a[p].rs;
  139.                         else
  140.                                 {
  141.                                         ans=p;
  142.                                         p=a[p].ls;
  143.                                 }
  144.                 }
  145.         return ans;
  146. }

  147. int main()
  148. {
  149.         cin>>n;
  150.         while(n--)
  151.                 {
  152.                         int opt, x;
  153.                         cin>>opt>>x;
  154.                         if(opt==1) Ins(rt, x);
  155.                         if(opt==2) Del(rt, x);
  156.                         if(opt==3) cout<<Rank(rt, x)<<endl;
  157.                         if(opt==4) cout<<Getkth(rt, x)<<endl;
  158.                         if(opt==5)
  159.                                 {
  160.                                         a[0].val=-inf;
  161.                                         cout<<a[Pre(rt, x)].val<<endl;
  162.                                 }
  163.                         if(opt==6) cout<<a[Suc(rt, x)].val<<endl;
  164.                         ///ArrayPr();
  165.                         ///BSTPr(rt);
  166.                 }
  167.         return 0;
  168. }

  169. /*
  170. 10
  171. 1 1
  172. 1 2
  173. 1 5
  174. 1 8
  175. 1 9
  176. 1 7
  177. 1 6
  178. 1 1
  179. 1 2
  180. 1 9
  181. */
复制代码
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|服务支持:DZ动力|华师一附中OI组  

GMT+8, 2024-12-25 15:05 , Processed in 0.200827 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表