[关闭]
@Cwen-oier 2018-07-30T19:39:58.000000Z 字数 2866 阅读 1084

平衡树

Splay基础操作

1.splay入门解析

参考yyb博客(yyb博客里没管第k大(故未记size),以下代码可供参考)
my 板子:(洛谷【模板】普通平衡树(Splay)

  1. int n,m,root,num;
  2. struct pp
  3. {
  4. int son[2],dad,it,cnt,size; //cnt:这个点有多少个值;size:子树上有多少个值
  5. }tr[_];//好像空间是O(n)吧?
  6. inline int read()
  7. {
  8. rg int save=0,w=1;rg char q=getchar(); //!!!!!! =getsonar() 不可少!!!! (否则q可能会有初值)
  9. while(q<'0'||q>'9'){if(q=='-')w=-1;q=getchar();}
  10. while(q>='0'&&q<='9')save=(save<<3)+(save<<1)+q-'0',q=getchar();
  11. return w*save;
  12. }
  13. inline void pushup(rg int x)
  14. {
  15. tr[x].size=tr[tr[x].son[0]].size+tr[tr[x].son[1]].size+tr[x].cnt;//加了两个儿子别忘了加自己
  16. }
  17. inline void doit(rg int x)//一个结点的旋转
  18. {
  19. rg int y=tr[x].dad,z=tr[y].dad;
  20. rg bool k=tr[y].son[1]==x;
  21. tr[x].dad=z;
  22. tr[z].son[tr[z].son[1]==y]=x;
  23. tr[y].son[k]=tr[x].son[!k];
  24. tr[tr[y].son[k]].dad=y;
  25. tr[y].dad=x;
  26. tr[x].son[!k]=y;
  27. pushup(y),pushup(x);//记住要更新size!!!
  28. }
  29. inline void splay(rg int x,rg int to)//把某节点旋转多次(或一次)成to的儿子
  30. {
  31. while(tr[x].dad!=to)
  32. {
  33. rg int y=tr[x].dad,z=tr[y].dad;
  34. //由于x与y在其父亲的同側时,如果只是一路把x转上去的话,有一条链始终没被拆,保证不了时间复杂度,所以我们这样做:
  35. if(z!=to) //
  36. ((tr[z].son[1]==y)^(tr[y].son[1]==x))?doit(x):doit(y);//在其父亲同側就转y,不在就没关系可以转x
  37. doit(x);
  38. }
  39. if(!to)root=x; //记得更新根节点!!!
  40. }
  41. inline void find(rg int x)//值为x的点
  42. {
  43. rg int now=root;
  44. if(!now)return;//看情况加不加此句
  45. while(tr[now].it!=x&&tr[now].son[x>tr[now].it])
  46. now=tr[now].son[x>tr[now].it];
  47. splay(now,0);
  48. }
  49. inline void insert(rg int x)
  50. {
  51. rg int now=root,fa=0;
  52. while(now&&tr[now].it!=x) //先从根节点往下找到第一个 值与x相等 或 不存在 的点
  53. fa=now,now=tr[now].son[x>tr[now].it]; //1: right son, 2: left son
  54. if(tr[now].it==x)
  55. tr[now].cnt++; //
  56. else
  57. {
  58. now=++num;
  59. tr[now].it=x,tr[now].cnt=1; //
  60. tr[now].dad=fa;
  61. tr[now].size=1;
  62. if(fa)
  63. tr[fa].son[x>tr[fa].it]=now;
  64. // tr[now].size++; //attention!!!反正在下一行的splay()中会进入pushup()函数而计算子树大小,所以无需提前算size
  65. }
  66. splay(now,0);
  67. }
  68. inline int Kth(rg int x)
  69. {
  70. rg int now=root;
  71. if(x>tr[root].size)return 0;
  72. while(666)
  73. {
  74. rg int front=tr[tr[now].son[0]].size+tr[now].cnt;
  75. if(x<=front&&x>tr[tr[now].son[0]].size)return tr[now].it;
  76. if(x<=tr[tr[now].son[0]].size)
  77. now=tr[now].son[0];
  78. else now=tr[now].son[1],x-=front;
  79. /* if(x>front)now=tr[now].son[1],x-=front;
  80. else return tr[now].it;
  81. */ }
  82. }
  83. inline int next(rg int x,rg bool b)
  84. {
  85. find(x);
  86. rg int now=root;
  87. if(x<tr[now].it&&b)return now;//返回的是指针啊喂
  88. if(x>tr[now].it&&(!b))return now;
  89. now=tr[now].son[b];
  90. while(tr[now].son[!b])now=tr[now].son[!b];
  91. return now;
  92. }
  93. inline void cut(rg int x)
  94. {
  95. rg int l=next(x,0);
  96. rg int r=next(x,1);
  97. splay(l,0);
  98. splay(r,l);
  99. rg int now=tr[r].son[0];
  100. if(tr[now].cnt>1)
  101. {
  102. tr[now].cnt--;
  103. splay(now,0);
  104. }
  105. else
  106. {
  107. if(tr[r].son[0]==num)--num;
  108. tr[r].son[0]=0;
  109. // pushup(r),pushup(root);
  110. //不用上上行的splay操作,但记得要pushup,不然假如下一个操作是求第k大,size没更新就可能错啦
  111. //但其实也不用pushup(你已经把前驱转到了根节点,这个点在后继的左儿子,求第k大的时候会考虑到它的size)
  112. }
  113. }
  114. int main()
  115. {
  116. n=read();
  117. rg int i,j;
  118. // for(i=1;i<=n;++i)tr[i].it=i,tr[i].son[1]=i+1,tr[i].dad=i-1;错误建树示范(必须不停旋转让树保持log(n)的树高,这样建条链不行)
  119. insert(2147483647),insert(-2147483647);//给树最两端的点以前驱和后继,方便删除它们
  120. for(i=1;i<=n;++i)
  121. {
  122. rg int op=read(),x=read();
  123. if(op==1)
  124. insert(x);
  125. if(op==2)
  126. cut(x);
  127. if(op==3)
  128. find(x),printf("%d\n",tr[tr[root].son[0]].size);
  129. if(op==4)
  130. printf("%d\n",Kth(x+1));
  131. if(op==5)
  132. printf("%d\n",tr[next(x,0)].it);
  133. if(op==6)
  134. printf("%d\n",tr[next(x,1)].it);
  135. }
  136. return 0;
  137. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注