华师一附中OI组

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

splay步步为营

[复制链接]

738

主题

1485

帖子

5422

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5422
跳转到指定楼层
楼主
发表于 2020-4-5 15:45:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
SPLAY是信息学奥赛高级数据结构里面一个很重要的内容,可以用来解决好多的实际问题,序列统计,序列翻转等,很多的BST之类的题目都可以用SPLAY来解决。为了学好它,我们步步为营一个个的做。

Splay的基本思想和并查集的有点相似,并查集在进行了查找的时候顺便压缩了路径,这是一个自调整的过程,Splay在查询的时候将这个点想转到根,因为树总在旋转,所以基本可以达到一个平衡。
Splay的精髓在于旋转,这个旋转和treap的有点相似,treap在插入的时候现先只考虑节点的val值,根据BST的特性插入到合适的位置,显然是一个叶节点,然后再比较它的rnd和父节点的rnd,若大了则旋转,这样一次旋转的话它的父亲变成了自己的儿子,它上升了一级,然后递归直到rnd满足要求。而splay的旋转也是一样,基本思想把它旋转到他父亲的位置,逐层向上直到它成为根节点。

上旋一层和treap中几乎是一样的,但是Spaly因为以后涉及到更多的操作,所以结构体中特加了一个fa域。

先来一个最简单的,只有插入和输出的splay,着重写好旋转,为了确保正确,分步手写LR和RR,免得出错。交到LG1097,AC了。基本说明旋转部分没问题。
  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 fa,s[2],sz;  ///s0s1左右儿子
  9. } a[N];
  10. int n, tot, root;
  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<<" s0 ";
  20.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].s[0];
  21.         cout<<endl<<" s1 ";
  22.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].s[1];
  23.         cout<<endl<<" sz ";
  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].s[0]>0)  BSTPr(a[rt].s[0]);
  30.         cout<<a[rt].val<<' '<<a[rt].cnt<<endl;
  31.         if (a[rt].s[1]>0)  BSTPr(a[rt].s[1]);
  32. }

  33. void LR(int rt)/// 左旋 因为有了fa,所以不需要传地址 但是也多了修改fa指针的操作
  34. {
  35.         ///cout<<"L\n";
  36.         int x=a[rt].fa; ///x是rt的爸爸
  37.         int y=a[x].fa;///y是x的爸爸 rt的爷爷
  38.         int l=a[rt].s[0]; ///rt的左儿子挂给x当右儿子(本来右儿子是rt)
  39.         a[x].s[1]=l;
  40.         if(l!=0) a[l].fa=x;        //这个要注意空节点没有fa

  41.         a[rt].fa=y; //rt取代了他的爸爸x
  42.         a[rt].s[0]=x;//rt的原父节点x成为rt 的左儿子
  43.         if(y!=0)
  44.                 {
  45.                         if(x==a[y].s[0])
  46.                                 a[y].s[0]=rt;
  47.                         else a[y].s[1]=rt;
  48.                 }
  49.         a[x].fa=rt;
  50. }

  51. void RR(int rt) /// 同上 右旋
  52. {
  53.         ///cout<<"R\n";
  54.         int x=a[rt].fa; ///x是rt的爸爸
  55.         int y=a[x].fa;///y是x的爸爸 rt的爷爷
  56.         int  r=a[x].s[0]=a[rt].s[1];
  57.         if(r!=0) a[r].fa=x;

  58.         a[rt].fa=y; //rt取代了他的爸爸x
  59.         a[rt].s[1]=x;
  60.         if(y!=0)
  61.                 {
  62.                         if(x==a[y].s[0])
  63.                                 a[y].s[0]=rt;
  64.                         else a[y].s[1]=rt;
  65.                 }
  66.         a[x].fa=rt;
  67. }

  68. void splay(int rt)//伸展操作: 6种情况
  69. {
  70.         int x,y;
  71.         while(a[rt].fa!=0)//反复进行旋转,直至将x节点调整至根部为止
  72.                 {
  73.                         x=a[rt].fa;///rt的爸爸
  74.                         y=a[x].fa;
  75.                         if(y==0)//在x为根的情况下,若x在左儿子位置,则右旋;否则左旋
  76.                                 {
  77.                                         if(rt==a[x].s[0]) RR(rt);
  78.                                         else LR(rt);
  79.                                         break;
  80.                                 }
  81.                     
  82.                         if(rt==a[x].s[0])//在x位于左位置的情况下,若x的父节点位于左位置,则分别
  83.                                 //以x的父节点和x为基准两次右旋;否则以x为基准右旋和左旋
  84.                                 {
  85.                                         if(x==a[y].s[0])  ///LLL
  86.                                                 {
  87.                                                         RR(x);
  88.                                                         RR(rt);
  89.                                                 }
  90.                                         else  ///LLR
  91.                                                 {
  92.                                                         RR(rt);
  93.                                                         LR(rt);
  94.                                                 }
  95.                                 }
  96.                         else
  97.                                 {
  98.                                         if(x==a[y].s[1]) ///RRR
  99.                                                 {
  100.                                                         LR(x);
  101.                                                         LR(rt);
  102.                                                 }
  103.                                         else
  104.                                                 {
  105.                                                         LR(rt);
  106.                                                         RR(rt);
  107.                                                 }
  108.                                 }
  109.                 }
  110.         root=rt;//新根
  111.         //cout<<rt<<endl;
  112. }
  113. void Ins(int rt, int x)   /// 在rt子树中插入x
  114. {
  115.         if(rt==0)  /// 新树
  116.                 {
  117.                         tot=root=1;
  118.                         a[1].fa=a[1].s[0]=a[1].s[1]=0;
  119.                         a[1].val=x;
  120.                         a[1].cnt=1;
  121.                 }
  122.         else if(a[rt].val==x) a[rt].cnt++;
  123.         else if(a[rt].val<x)
  124.                 {
  125.                         if (a[rt].s[1]==0)
  126.                                 {
  127.                                         a[rt].s[1]=++tot;
  128.                                         a[tot].s[0]=a[tot].s[1]=0;
  129.                                         a[tot].val=x;
  130.                                         a[tot].fa=rt;
  131.                                         a[tot].cnt=1;
  132.                                         splay(tot);
  133.                                 }
  134.                         else
  135.                                 Ins(a[rt].s[1],x);
  136.                 }
  137.         else if(a[rt].val>x)
  138.                 {
  139.                         if (a[rt].s[0]==0)
  140.                                 {
  141.                                         a[rt].s[0]=++tot;
  142.                                         a[tot].s[0]=a[tot].s[1]=0;
  143.                                         a[tot].val=x;
  144.                                         a[tot].fa=rt;
  145.                                         a[tot].cnt=1;
  146.                                         splay(tot);
  147.                                 }
  148.                         else
  149.                                 Ins(a[rt].s[0], x);
  150.                 }

  151. }

  152. int main()
  153. {
  154.         cin>>n;
  155.         while(n--)
  156.                 {
  157.                         int x;
  158.                         cin>>x;
  159.                         Ins(root,x);

  160.                 }
  161.         BSTPr(root);
  162.         return 0;
  163. }

  164. /*
  165. 10
  166. 1 2 5 8 9 7 6 1 2 9
  167. */
复制代码



这里写的代码很清晰直观,所以代码量比较大,加之我在里面写了很多的调试和测试语句,显得更长了。但是思路简单,主要是熟悉SPLAY的基本思路和几种旋转。要在纸上配合画图来理解。


回复

使用道具 举报

738

主题

1485

帖子

5422

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5422
推荐
 楼主| 发表于 2020-4-5 16:19:06 | 只看该作者
现在精简这部分的代码,用一个函数R(x)来表示将x向上旋一格,Splay(x,y)表示把x旋到y的位置,便于以后扩展的时候代码写短一点,因为比较复杂,里面加了好多的调试语句。
  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 fa,s[2],sz;  ///s0s1左右儿子
  9. } a[N];
  10. int n, tot, root;
  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<<" s0 ";
  20.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].s[0];
  21.         cout<<endl<<" s1 ";
  22.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].s[1];
  23.         cout<<endl<<" fa ";
  24.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].fa;
  25.         cout<<endl;
  26.         int kk;
  27.         cin>>kk;
  28. }

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

  35. int Son(int x)///判断x是L还是R儿子
  36. {
  37.         int y=a[x].fa;
  38.         return  a[y].s[1]==x;
  39. }
  40. void R(int x)
  41. {
  42.         int y=a[x].fa;
  43.         int z=a[y].fa;
  44.         int k=Son(x);
  45.         int v=a[x].s[k^1];///v是x另外一边的儿子将要被挂在y下面
  46.    
  47.         a[y].s[k]=v,a[v].fa=y;///v挂在y下  要是v=0这里省略了
  48.         a[z].s[Son(y)]=x,a[x].fa=z;///x挂在z下
  49.         a[x].s[k^1]=y,a[y].fa=x;///y挂在x下
  50.         /// 这三步需要画图理解
  51.   
  52.         ///cout<<"R"<<k<<endl;///0表示LR 1表示RR
  53. }
  54. inline void Splay(int x,int rt=root)  ///把x旋到rt的位置 默认rt是根
  55. {
  56.         rt=a[rt].fa;
  57.         int y,z;
  58.         while(a[x].fa!=rt)  /// y==rt就到位
  59.                 {
  60.                         y=a[x].fa,z=a[y].fa;;
  61.                         ///ArrayPr();
  62.                         ///cout<<x<<' '<<y<<' '<<z<<' '<<endl;

  63.                         if(z!=rt)
  64.                                 if(Son(x)==Son(y))R(y); /// 一字
  65.                                 else R(x);
  66.                         ///ArrayPr();
  67.                         R(x);
  68.                         ///ArrayPr();
  69.                 }
  70.         ///cout<<"Splay Complete!\n";
  71.         if(rt==0)root=x;
  72.         ///检查
  73.         ///ArrayPr();
  74.         ///BSTPr(root);
  75. }

  76. void Ins(int rt, int x)   /// 在rt子树中插入x
  77. {
  78.         if(a[rt].val==x) a[rt].cnt++;
  79.         else
  80.                 {
  81.                         int k=a[rt].val>x;
  82.                         {
  83.                                 if (a[rt].s[k^1]==0)
  84.                                         {
  85.                                                 a[rt].s[k^1]=++tot;
  86.                                                 a[tot].s[0]=a[tot].s[1]=0;
  87.                                                 a[tot].val=x;
  88.                                                 a[tot].fa=rt;
  89.                                                 a[tot].cnt=1;
  90.                                                 Splay(tot,root);
  91.                                         }
  92.                                 else
  93.                                         Ins(a[rt].s[k^1],x);
  94.                         }
  95.                 }

  96. }

  97. int main()
  98. {
  99.         int i;
  100.         cin>>n;
  101.         //插入-inf和inf
  102.         root=1;
  103.         tot=2;
  104.         a[1].val=-inf,a[1].s[0]=0,a[1].s[1]=2,a[1].fa=0;
  105.         a[2].val= inf,a[2].s[0]=0,a[2].s[1]=0,a[2].fa=1;

  106.         while(n--)
  107.                 {
  108.                         int x;
  109.                         cin>>x;
  110.                         Ins(root,x);

  111.                         ///BSTPr(root);
  112.                 }
  113.         BSTPr(root);
  114.         return 0;
  115. }

  116. /*
  117. 10
  118. 1 2  5  8  9  7  6  1  2 9
  119. */
复制代码



这个程序我还好久没有写了,以前都是写LR RR那样清晰明了的,现在把他们合N为一,代码段肯定是短了,但是有错的话就很难调出来,你看我的程序里面都保留了我曾经的调试痕迹,体会下我是怎么样步步为营做程序的。

直观的加了-inf和-inf,也用cnt控制了它们的时候输出,交到LG1097,AC了。
回复 支持 1 反对 0

使用道具 举报

738

主题

1485

帖子

5422

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5422
板凳
 楼主| 发表于 2020-4-10 18:01:53 | 只看该作者
现在插入和遍历都是正确的,特别检查了旋转,既然LG1097AC了,基本既没有问题,现在添上删除部分,要删除x的话需要先查找到x所在的节点,找到后旋转到根,设为root,然后看它有没有左子树l,有的话,把左子树中的最大节点p,旋转到此节点l的位置,那么这个树l就完全没有右子树了,因为根是最大嘛!然后把root的右子树r作为l的右子树,接到l上,修改下root,新根为l。
  1. int Find(int rt,int x)  /// 找到x对应的a[rt]
  2. {
  3.         if (rt==0) return 0;
  4.         if (a[rt].val==x)
  5.                 {
  6.                         Splay(rt,root);
  7.                         return rt;
  8.                 }
  9.         int k=a[rt].val<x; /// k=0表示左子树中去1表示去右子树
  10.         return Find(a[rt].s[k],x);
  11. }
  12. void Del(int x) //删除节点
  13. {
  14.         int rt=Find(root,x);///找到对应的rt
  15.         if(rt==0) return;
  16.         if(a[rt].cnt>1)  a[rt].cnt--;
  17.         else if(a[rt].s[0]==0) /// 没有左子树
  18.                 {
  19.                         root=a[rt].s[1];
  20.                         a[root].fa=0;
  21.                 }
  22.         else
  23.                 {
  24.                         int p,l,r;
  25.                         l=a[rt].s[0];
  26.                         r=a[rt].s[1];
  27.                         /// 左子树里面去找最右的儿子
  28.                         p=l;
  29.                         while(a[p].s[1]>0) p=a[p].s[1];
  30.                         Splay(p,l); ///转上来作为根的左子树
  31.                         /// 因为最大,没有右子树,把他作为新根
  32.                         a[p].s[1]=r;  ///rt的右儿子接上去
  33.                         a[r].fa=p;
  34.                         a[root=p].fa=0;/// 新根
  35.                         pushup(p);
  36.                 }
  37.         ///要不要回收?
  38. }
复制代码

检查了下这段,把刚才插入的结点无聊的乱序一个个的删除,发现真的都删除了。

回复 支持 反对

使用道具 举报

738

主题

1485

帖子

5422

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5422
地板
 楼主| 发表于 2020-4-11 13:21:23 | 只看该作者
因为Splay强调了R过程,所以查询排名和Treap有点不同,SPLAY查询的时候带着了旋转。
要想查找x的排名,先要找到x,当找到x的时候,其实x已经被旋到了根,现在只要直接输出左子树下的sz就行了。但是这里我们有两个假节点 +/-inf ,所以真实的排名要调整。
  1. int Rank(int x) ///在RT子树中比x小的数 +1
  2. {
  3.         int rt=Find(root,x);
  4.         int l=a[rt].s[0];
  5.         return a[l].sz;/// 包含那个-inf 所以不需要+1
  6. }
复制代码


回复 支持 反对

使用道具 举报

738

主题

1485

帖子

5422

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5422
5#
 楼主| 发表于 2020-4-23 18:27:29 | 只看该作者
至于求前驱和后继,和以前的一样,但是这次我们选择使用非递归的写法,
  1. int Pre(int rt, int x)
  2. {
  3.         int p=rt, ans;
  4.         while(p>0)
  5.                 {
  6.                         if(x<=a[p].val) p=a[p].s[0];
  7.                         else
  8.                                 {
  9.                                         ans=p;
  10.                                         p=a[p].s[1];
  11.                                 }
  12.                 }
  13.         return ans;
  14. }
  15. int Suc(int rt, int x)
  16. {
  17.         int p=rt, ans;
  18.         while(p>0)
  19.                 {
  20.                         if(a[p].val<=x) p=a[p].s[1];
  21.                         else
  22.                                 {
  23.                                         ans = p;
  24.                                         p=a[p].s[0];
  25.                                 }
  26.                 }
  27.         return ans;
  28. }
复制代码
回复 支持 反对

使用道具 举报

738

主题

1485

帖子

5422

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5422
6#
 楼主| 发表于 2020-4-23 18:36:37 | 只看该作者
结合上面所有的代码,3369的做法如下,交上去,顺利AC.

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int inf=99999999;
  4. const int N=100100;
  5. struct node
  6. {
  7.         int val,cnt;
  8.         int fa,s[2],sz;  ///s0s1左右儿子
  9. } a[N];
  10. int n, tot, root;
  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<<" s0 ";
  20.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].s[0];
  21.         cout<<endl<<" s1 ";
  22.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].s[1];
  23.         cout<<endl<<" fa ";
  24.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].fa;
  25.         cout<<endl<<" sz ";
  26.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].sz;
  27.         cout<<endl;
  28. }

  29. void BSTPr(int rt)   ///中序输出以rt为根的BST
  30. {
  31.         if (a[rt].s[0]>0)  BSTPr(a[rt].s[0]);
  32.         if (a[rt].cnt>0) cout<<a[rt].val<<' '<<a[rt].cnt<<endl;
  33.         if (a[rt].s[1]>0)  BSTPr(a[rt].s[1]);
  34. }
  35. void pushdown(int rt)   ///类似线段树网上调整
  36. {
  37.         ///暂时无用
  38. }
  39. void pushup(int rt)  ///类似线段树网上调整
  40. {
  41.         int l=a[rt].s[0], r=a[rt].s[1];
  42.         a[rt].sz=a[l].sz+a[r].sz+a[rt].cnt;
  43. }
  44. int Son(int x)///判断x是L还是R儿子
  45. {
  46.         int y=a[x].fa;
  47.         return  a[y].s[1]==x;
  48. }
  49. void R(int x)
  50. {
  51.         int y=a[x].fa;
  52.         int z=a[y].fa;
  53.         int k=Son(x);
  54.         int v=a[x].s[k^1];///v是x另外一边的儿子将要被挂在y下面
  55.         pushdown(x),pushdown(y);///下传
  56.         a[y].s[k]=v,a[v].fa=y;///v挂在y下  要是v=0这里省略了
  57.         a[z].s[Son(y)]=x,a[x].fa=z;///x挂在z下
  58.         a[x].s[k^1]=y,a[y].fa=x;///y挂在x下
  59.         /// 这三步需要画图理解
  60.         pushup(y),pushup(x);///上传,注意顺序
  61. }
  62. inline void Splay(int x,int rt=root)  ///把x旋到rt的位置 默认rt是根
  63. {
  64.         rt=a[rt].fa;
  65.         int y,z;
  66.         while(a[x].fa!=rt)  /// y==rt就到位
  67.                 {
  68.                         y=a[x].fa,z=a[y].fa;;
  69.                         if(z!=rt)
  70.                                 if(Son(x)==Son(y))R(y); /// 一字
  71.                                 else R(x);
  72.                         R(x);
  73.                 }
  74.         if(rt==0)root=x;
  75. }
  76. int Find(int rt,int x)  /// 找到x对应的a[rt]
  77. {
  78.         if (a[rt].val==x)
  79.                 {
  80.                         Splay(rt,root);
  81.                         return rt;
  82.                 }
  83.         int k=a[rt].val<x; /// k=0表示左子树中去1表示去右子树
  84.         if (a[rt].s[k]>0)  return Find(a[rt].s[k],x);
  85.         else return 0;
  86. }
  87. void Ins(int rt, int x)   /// 在rt子树中插入x
  88. {
  89.         a[rt].sz++;
  90.         if(a[rt].val==x) a[rt].cnt++;
  91.         else
  92.                 {
  93.                         int k=(a[rt].val<x);/// k=0表示左子树中去1表示去右子树
  94.                         if (a[rt].s[k]==0)
  95.                                 {
  96.                                         a[rt].s[k]=++tot;
  97.                                         a[tot].s[0]=a[tot].s[1]=0;
  98.                                         a[tot].val=x;
  99.                                         a[tot].fa=rt;
  100.                                         a[tot].cnt=a[tot].sz=1;
  101.                                         Splay(tot,root);
  102.                                 }
  103.                         else
  104.                                 Ins(a[rt].s[k],x);
  105.                 }

  106. }
  107. void Del(int x) //删除节点
  108. {
  109.         int rt=Find(root,x);///找到对应的rt
  110.         if(rt==0) return;
  111.         a[rt].sz--;
  112.         if(a[rt].cnt>1)  a[rt].cnt--;
  113.         else if(a[rt].s[0]==0) /// 没有左子树
  114.                 {
  115.                         root=a[rt].s[1];
  116.                         a[root].fa=0;
  117.                 }
  118.         else
  119.                 {
  120.                         int p,l,r;
  121.                         l=a[rt].s[0];
  122.                         r=a[rt].s[1];
  123.                         /// 左子树里面去找最右的儿子
  124.                         p=l;
  125.                         while(a[p].s[1]>0) p=a[p].s[1];
  126.                         Splay(p,l); ///转上来作为根的左子树
  127.                         /// 因为最大,没有右子树,把他作为新根
  128.                         a[p].s[1]=r;  ///rt的右儿子接上去
  129.                         a[r].fa=p;
  130.                         a[root=p].fa=0;;/// 新根
  131.                         pushup(p);
  132.                 }
  133.         ///要不要回收?
  134. }
  135. int Rank(int x) ///在RT子树中比x小的数 +1
  136. {
  137.         int rt=Find(root,x);
  138.         int l=a[rt].s[0];
  139.         return a[l].sz;/// 包含那个-inf 所以不需要+1
  140. }

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

  148. int Pre(int rt, int x)
  149. {
  150.         int p=rt, ans;
  151.         while(p>0)
  152.                 {
  153.                         if(x<=a[p].val) p=a[p].s[0];
  154.                         else
  155.                                 {
  156.                                         ans=p;
  157.                                         p=a[p].s[1];
  158.                                 }
  159.                 }
  160.         return ans;
  161. }
  162. int Suc(int rt, int x)
  163. {
  164.         int p=rt, ans;
  165.         while(p>0)
  166.                 {
  167.                         if(a[p].val<=x) p=a[p].s[1];
  168.                         else
  169.                                 {
  170.                                         ans = p;
  171.                                         p=a[p].s[0];
  172.                                 }
  173.                 }
  174.         return ans;
  175. }
  176. int main()
  177. {
  178.         ///freopen("3369.in","r",stdin);
  179.         ///freopen("3369.out","w",stdout);
  180.         int x,opt;
  181.         cin>>n;
  182.         //插入-inf和inf
  183.         root=1;
  184.         tot=2;
  185.         a[1].val=-inf,a[1].s[0]=0,a[1].s[1]=2,a[1].fa=0;
  186.         a[2].val= inf,a[2].s[0]=0,a[2].s[1]=0,a[2].fa=1;
  187.         a[1].sz=2,a[2].sz=1;
  188.         a[1].cnt=a[2].cnt=1;

  189.         while(n--)
  190.                 {
  191.                         cin>>opt>>x;
  192.                         if(opt==1) Ins(root, x);
  193.                         if(opt==2) Del(x);
  194.                         if(opt==3) cout<<Rank(x)<<endl;
  195.                         if(opt==4) cout<<Getkth(root, x+1)<<endl;
  196.                         if(opt==5) cout<<a[Pre(root, x)].val<<endl;
  197.                         if(opt==6) cout<<a[Suc(root, x)].val<<endl;
  198.                 }
  199.         return 0;
  200. }
  201. /*
  202. 40
  203. 1 3 7 5 9 2 8 4 6 0
  204. 11 13 17 15 19 12 18 14 16 10
  205. 31 33 37 35 39 32 38 34 36 30
  206. 11 13 17 15 19 12 18 14 16 10
  207. */
复制代码
回复 支持 反对

使用道具 举报

738

主题

1485

帖子

5422

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5422
7#
 楼主| 发表于 2020-4-24 13:04:20 | 只看该作者
上面的例子是SPLAY的一个应用,就是把某个点旋转到根,在查询的过程中总在转动的话这棵树的平衡性不会太差,所以可以当AVL来用。还有一个更牛的应用就是伸展操作,把
在学这个的时候要注意转换一下思想,把区间的下标当做树的节点。
一个典型的简单应用基础题是P3391 文艺平衡树 http://hsyit.cn/forum.php?mod=viewthread&tid=69341
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-12-26 13:40 , Processed in 0.104650 second(s), 26 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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