经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
平衡树 Treap & Splay [学习笔记]
来源:cnblogs  作者:lty_ylzsx  时间:2024/5/27 9:21:18  对本文有异议

平衡树 \(\tt{Treap}\) & \(\tt{Splay}\)

壹.单旋 \(\tt{Treap}\)

首先了解 \(\tt{BST}\)

非常好用的东西,但是数据可以把它卡成一条链 \(\dots\)

于是,我们将 \(\tt{Tree}\)\(\tt{heap}\) (堆) 合并,以保证平衡树 \(\log\) 的深度。

具体地,我们可以使用旋转操作实现

K8He的图

以右旋为例,我们发现,本来的中序遍历顺序为 \(y<p<x<q<z<r\),那么对于 \(q\) 右旋,即将左儿子旋上来,由于本来 \(p<q\) ,所以显然 \(q\) 要成为 \(p\) 的右儿子。那就剩下 \(x\) 无家可归,我们发现 \(p<x<q\),那么 \(q\) 的左儿子再适合不过了。

我们规定 \(0\) 方向为左,\(1\) 方向为右,即可通过 \(d\) ^ \(1\) 实现方向取反。

一般的,对于一个节点 \(i\),如果将其 \(d\) 方向上的儿子 \(s\) 旋上去,那么 \(i\) 要成为 \(s\)\(d\) ^ \(1\) 方向上的儿子,\(s\) 原来在 \(d\) ^ \(1\) 方向上的儿子要成为 \(i\)\(d\) 方向上的儿子。

  1. void rotate(int &i,int d){
  2. int s=t[i].son[d];
  3. t[i].son[d]=t[s].son[d^1];
  4. t[s].son[d^1]=i;
  5. up(i),i=s,up(i);
  6. return;
  7. }

那么我们什么时候进行旋转呢?还记得我们说过要利用堆的性质,那么我们对每个节点随机一个优先级,将它按照小根堆或大根堆存,若当前不满足堆的性质了,那就旋转。

  • 插入操作,从根往下跑,但要注意不满足堆的性质时,考虑旋转。
  1. void insert(int &i,int k){
  2. if(!i){
  3. i=++tot;
  4. t[i].cnt=t[i].siz=1;
  5. t[i].val=k,t[i].rd=rnd();
  6. return;
  7. }
  8. t[i].siz++;
  9. if(t[i].val==k){
  10. ++t[i].cnt;return;
  11. }
  12. int d=(t[i].val<k);
  13. insert(t[i].son[d],k);
  14. if(t[i].rd>t[t[i].son[d]].rd) rotate(i,d);
  15. return;
  16. }
  • 删除操作,先找节点,如果只有一个儿子,让儿子替换它,否则让儿子旋上来(当然要满足堆性质),然后一直旋,直到剩一个儿子或者成为叶子节点。
  1. void del(int &i,int k){
  2. if(!i) return;
  3. if(t[i].val==k){
  4. if(t[i].cnt>1){
  5. --t[i].cnt,--t[i].siz;
  6. return;
  7. }
  8. int d=(t[ls(i)].rd>t[rs(i)].rd);
  9. if(!ls(i)||!rs(i)) i=ls(i)+rs(i);
  10. else rotate(i,d),del(i,k);
  11. return;
  12. }
  13. t[i].siz--;
  14. int d=t[i].val<k;
  15. del(t[i].son[d],k);
  16. return;
  17. }
  • 查排名,看代码理解即可。
  1. int rk(int i,int k){
  2. if(!i) return 1;
  3. if(t[i].val>k) return rk(ls(i),k);
  4. if(t[i].val<k) return t[ls(i)].siz+t[i].cnt+rk(rs(i),k);
  5. return t[ls(i)].siz+1;
  6. }
  • 查第 \(k\) 大,思想和线段树一样。
  1. int kth(int i,int k){
  2. while(1){
  3. if(k<=t[ls(i)].siz) i=ls(i);
  4. else if(k>t[ls(i)].siz+t[i].cnt)
  5. k-=(t[ls(i)].siz+t[i].cnt),i=rs(i);
  6. else return t[i].val;
  7. }
  8. }
  • 前驱后继,和普通 \(\tt{BST}\) 一样。
  1. int pre(int i,int k){
  2. if(!i) return -1e8;
  3. if(t[i].val>=k) return pre(ls(i),k);
  4. return max(pre(rs(i),k),t[i].val);
  5. }
  6. int nex(int i,int k){
  7. if(!i) return 1e8;
  8. if(t[i].val<=k) return nex(rs(i),k);
  9. return min(nex(ls(i),k),t[i].val);
  10. }

对于单旋 \(\tt{Treap}\),我们只需要理解旋转操作即可,毕竟下面的 \(\tt{Splay}\) 还要用它,请务必看懂旋转操作,其他的,还是FHQ好打, 差不多看看就行,应用范围不大。

(板子封装在下面题单 普通平衡树 里)


贰.无旋 \(\tt{FHQ\ Treap}\)

由于单旋 \(\tt{Treap}\) 不好打且扩展功能不多,所以我们引入新的 \(\tt{FHQ\_ Treap}\),好像是神范浩强发明的,%%%%%%。

网上都说FHQ比单旋好理解,我表示理解了之后确实好理解,但你得先理解(我看了一个多小时才看懂,不过我是fw)

好那么直入正题 —— \(\tt{FHQ\_ Treap}\)

既然也是 \(\tt{Treap}\),那就是一样的,也是靠堆性质,它的不同之处就在于,它无旋,它是靠分裂+合并来保证 \(\log\) 的深度。

具体地,分裂方式有两种,一种是按权值分裂,另一种是按照子树大小分裂:

  • 按照权值分裂,比如将以 \(i\) 为根的平衡树分成两棵平衡树,根分别是 \(x,y\),要求树 \(x\) 的权值都小于等于 \(k\),剩下是 \(y\),那么分讨:
    • 如果 \(val(i)<=k\),那么 \(i\) 的整棵左子树一定都小于 \(k\),肯定都要划到 \(x\) 里,则令 \(x=i\),继续递归划分 \(rs(i)\) 即可。
    • 否则,\(i\) 的整棵右子树一定都大于 \(k\),肯定都要划到 \(y\) 里,则令 \(y=i\),继续递归划分 \(ls(i)\) 即可。

注意取地址。

  1. void split(int i,int k,int &x,int &y){
  2. if(!i){x=y=0;return;}
  3. if(val(i)>k) y=i,split(ls(i),k,x,ls(i));
  4. if(val(i)<=k) x=i,split(rs(i),k,rs(i),y);
  5. up(i);return;
  6. }
  • 按照子树大小分裂,还是将以 \(i\) 为根的平衡树分成两棵平衡树,根分别是 \(x,y\),要求是 \(siz(x)=k\),还是和上面一样:
    • 如果 \(siz(ls(i))+cnt(i)<=k\),那么 \(i\) 的整棵左子树和 \(i\) 肯定都要划到 \(x\) 里,则令 \(x=i\),继续递归划分 \(rs(i)\) 即可。
    • 否则,\(i\) 的整棵右子树肯定都要划到 \(y\) 里,则令 \(y=i\),继续递归划分 \(ls(i)\) 即可。

按子树大小分裂,一般用在平衡树维护序列,后面的 \(\tt{Splay}\) 也是一样。

  1. void split(int i,int k,int &x,int &y){
  2. if(!i){x=y=0;return;}
  3. if(siz(ls(i))+cnt(i)<=k) x=i,split(rs(i),k-(siz(ls(i))+cnt(i)),rs(i),y);
  4. else y=i,split(ls(i),k,x,ls(i));
  5. up(i);
  6. }
  • 下一个操作是合并,\(\tt{FHQ\_ Treap}\) 正是通过它保证的堆性质,设要合并的两棵树的根分别为 \(x,y\),设堆性质为大根堆。
    • \(rd(x)>rd(y)\) 则把 \(x\) 定为根,然后继续递归合并 \(rs(x)\)\(y\)
    • 否则把 \(y\) 定为根,然后继续递归合并 \(x\)\(ls(y)\)
  1. void merge(int &i,int x,int y){
  2. if(!x||!y){i=x|y;return;}
  3. if(rd(x)>rd(y)) merge(rs(x),rs(x),y),i=x;
  4. else merge(ls(y),x,ls(y)),i=y;
  5. up(i);return;
  6. }
  • 插入 \(k\),先分裂出 \(<=k-1\),合并时把 \(k\) 合并进去。
  1. void insert(int k){
  2. int rt1,rt2;
  3. split(rt,k-1,rt1,rt2);
  4. merge(rt,rt1,New(k));merge(rt,rt,rt2);
  5. return;
  6. }
  • 删除 \(k\),把 \(k\) 分裂出来,然后把 \(k\) 用它的左右子树合并替代,再合并。
  1. void del(int k){
  2. int rt1,rt2,cut;
  3. split(rt,k-1,rt1,rt2);split(rt2,k,cut,rt2);
  4. merge(cut,ls(cut),rs(cut));
  5. merge(rt,rt1,cut);merge(rt,rt,rt2);
  6. return;
  7. }
  • 查排名,分裂完 \(\leq k-1\) 的树大小 \(+1\)
  1. int rk(int i,int k){
  2. int rt1,rt2,res;
  3. split(i,k-1,rt1,rt2);
  4. res=siz(rt1)+1;
  5. merge(i,rt1,rt2);
  6. return res;
  7. }
  • \(k\) 小,和普通 \(\tt{Treap}\) 一样。

  • 前驱后继,以前驱为例,分裂出 \(\leq k-1\),那么这部分都小于 \(k\),找出最大值即可,一直跑右儿子,后继同理。

  1. int pre(int &i,int k){
  2. int rt1,rt2,res;
  3. split(i,k-1,rt1,rt2),res=rt1;
  4. while(rs(res)) res=rs(res);
  5. merge(i,rt1,rt2);
  6. return val(res);
  7. }
  8. int nxt(int &i,int k){
  9. int rt1,rt2,res;
  10. split(i,k,rt1,rt2),res=rt2;
  11. while(ls(res)) res=ls(res);
  12. merge(i,rt1,rt2);
  13. return val(res);
  14. }

(板子封装在下面题单 普通平衡树 里)


叁.双旋 \(\tt{Splay}\)

\(\tt{Splay}\) 不同于以上两种 \(\tt{Treap}\),它不再依靠随机的优先级保证深度,而是通过不断旋转来达到目的。

类似于单旋,只不过单旋是将某节点的儿子旋上来,而 \(\tt{Splay}\) 是将某节点自身旋上去,单次旋转和 \(\tt{Treap}\) 一样,但是要多记录一个父亲

具体地,旋转 \(x\) 时,令 \(y\)\(x\) 的父亲,\(z\) 为祖父,设 \(x\)\(y\)\(d\) 方向上的儿子,则单次旋转可分为这几步:

  • \(x\) 替换 \(y\) 成为 \(z\) 的儿子
  • \(x\)\(d\) ^ \(1\) 方向的儿子下放给 \(y\)\(d\) 方向的儿子
  • \(y\) 充当 \(x\)\(d\) ^ \(1\) 方向的儿子

三次修改,三次认爹,rotate就写完了

  1. #define ds(i) t[i].son[d]
  2. #define bs(i) t[i].son[d^1]
  3. void rotate(int x){
  4. int y=fa(x),z=fa(y);
  5. int d=(rs(y)==x);
  6. t[z].son[(rs(z)==y)]=x;fa(x)=z;
  7. ds(y)=bs(x);fa(bs(x))=y;
  8. bs(x)=y;fa(y)=x;
  9. up(y),up(x);
  10. }

然后便是 \(\tt{Splay}\) 的核心操作,splay 如说

具体地,splay 操作是将节点 \(x\) 旋转到目标节点 \(s\) 的儿子,若 \(s=0\) 则为旋转到根。那么如果我们一直一直单旋上去的话我们会发现一个严重的问题——虽然 \(x\) 上去了,但是它的最大深度依然没变,也就是说,转了个寂寞。。

那么怎么办,进行双旋,讨论几种情况——(\(x,y,z\) 意义同上)

  • \(z=s\) 直接将 \(x\) 单旋一次上去

  • \(z\not ={s}\)

    • \(x,y,z\) 三点共线,即

    此时我们应先转 \(y\) 再转 \(x\)

    -

    • \(x,y,z\) 三点不共线,直接旋转两次 \(x\)

    -

就这样旋旋旋,就能保证深度OK,每次插入节点后都要进行一次 Splay

  1. void splay(int x,int s){
  2. while(fa(x)!=s){
  3. int y=fa(x),z=fa(y);
  4. if(z!=s)
  5. (ls(y)==x)^(ls(z)==y)?rotate(x):rotate(y);
  6. rotate(x);
  7. }
  8. if(!s) rt=x;
  9. }

至于这么旋为什么可以让复杂度OK,使用什么"势能分析法",我是fw我不会。

\(\tt{Splay}\)\(\tt{FHQ}\) 一样,也是两种维护方式,一种维护权值,一种维护下标(即序列的中序遍历)。

然后就是 \(\tt{Splay}\) 的一些基本操作:

  • 插入,有两种方式,即按权值和子树大小,与 \(\tt{FHQ}\) 类似,注意要记录一下父亲节点

    • 按照权值插入
    1. void insert(int k){
    2. int p=rt,f=0;
    3. while(p&&val(p)!=k){
    4. f=p;
    5. p=t[p].son[val(p)<k];
    6. }
    7. if(p) ++cnt(p);
    8. else{
    9. p=++tot;
    10. if(f) t[f].son[val(f)<k]=p;
    11. val(p)=k;fa(p)=f;
    12. siz(p)=cnt(p)=1;
    13. }
    14. splay(p,0);
    15. }
    • 按照子树大小插入,可以递归写(爱咋写咋写)
    1. void insert(int &i,int f,int x,int k){
    2. if(!i){
    3. i=++tot;
    4. siz(i)=1;fa(i)=f;val(i)=k;
    5. return;
    6. }
    7. if(x<=siz(ls(i))+1) insert(ls(i),i,x,k);
    8. else insert(rs(i),i,x-siz(ls(i))-1,k);
    9. up(i);
    10. }
  • 对于splay,我们要先找到某权值对应的节点,直接找然后splay

    • 权值
    1. void find(int k){
    2. if(!rt) return;
    3. int p=rt;
    4. while(t[p].son[val(p)<k]&&val(p)!=k){
    5. p=t[p].son[val(p)<k];
    6. }
    7. splay(p,0);
    8. }
    • 子树大小
    1. void find(int x){
    2. if(!rt) return;
    3. int p=rt;
    4. while(siz(ls(p))+1!=x){
    5. if(x<=siz(ls(p))+1){
    6. p=ls(p);
    7. }
    8. else{
    9. x-=(siz(ls(p))+1);
    10. p=rs(p);
    11. }
    12. }
    13. splay(p,0);
    14. }
  • 查第 \(k\) 小,与 \(\tt{Treap}\) 同理,不再赘述

  • 查排名,转到根节点后左子树的大小 \(+1\) 即可

  • 查前驱后继,以前驱为例,转到根之后左子树里最大值即前驱,后继同理

  • 删除比较有意思,我们先找到前驱后继,然后将前驱splay到根,将后继splay到前驱的右儿子,那么要删除的节点就一定为 \(ls(rs(rt))\) (如下图)。这也就意味着必须有前驱后继,否则删不了,那么直接插入两个极值哨兵节点即可。

  1. pre
  2. / ... nxt
  3. / cut ...
  1. void del(int k){
  2. int prek=pre(k);
  3. int nxtk=nxt(k);
  4. splay(prek,0);splay(nxtk,prek);
  5. int cut=ls(nxtk);
  6. if(cnt(cut)>1)
  7. --cnt(cut),splay(cut,0);
  8. else ls(nxtk)=0;
  9. }

另外,维护序列的 \(\tt{Splay}\) 进行区间操作时,也是将区间转化为子树,和删除操作类似,比如文艺平衡树就是这样,不再赘述。

最后注意一定要插哨兵

(板子封装在下面题单 普通平衡树 里)


肆.\(hs\)题单

\(T_D\) 普通平衡树

由于是纯板子,所以先挂 \(T_D\)

普通Treap
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define read read()
  4. #define pt puts("")
  5. inline int read{
  6. int x=0,f=1;char c=getchar();
  7. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  8. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  9. return f*x;
  10. }
  11. void write(int x){
  12. if(x<0) putchar('-'),x=-x;
  13. if(x>9) write(x/10);
  14. putchar(x%10+'0');
  15. return;
  16. }
  17. #define N 100010
  18. int m;
  19. namespace TREAP{
  20. mt19937 rnd(0x7f);
  21. struct Treap{
  22. int son[2],cnt,siz,val,rd;
  23. #define ls(i) t[i].son[0]
  24. #define rs(i) t[i].son[1]
  25. }t[N];
  26. int tot,rt;
  27. void up(int i){
  28. t[i].siz=t[ls(i)].siz+t[rs(i)].siz+t[i].cnt;
  29. }
  30. void rotate(int &i,int d){
  31. int s=t[i].son[d];
  32. t[i].son[d]=t[s].son[d^1];
  33. t[s].son[d^1]=i;
  34. up(i),i=s,up(i);
  35. return;
  36. }
  37. void insert(int &i,int k){
  38. if(!i){
  39. i=++tot;
  40. t[i].cnt=t[i].siz=1;
  41. t[i].val=k,t[i].rd=rnd();
  42. return;
  43. }
  44. t[i].siz++;
  45. if(t[i].val==k){
  46. ++t[i].cnt;return;
  47. }
  48. int d=(t[i].val<k);
  49. insert(t[i].son[d],k);
  50. if(t[i].rd>t[t[i].son[d]].rd) rotate(i,d);
  51. return;
  52. }
  53. void del(int &i,int k){
  54. if(!i) return;
  55. if(t[i].val==k){
  56. if(t[i].cnt>1){
  57. --t[i].cnt,--t[i].siz;
  58. return;
  59. }
  60. int d=(t[ls(i)].rd>t[rs(i)].rd);
  61. if(!ls(i)||!rs(i)) i=ls(i)+rs(i);
  62. else rotate(i,d),del(i,k);
  63. return;
  64. }
  65. t[i].siz--;
  66. int d=t[i].val<k;
  67. del(t[i].son[d],k);
  68. return;
  69. }
  70. int rk(int i,int k){
  71. if(!i) return 1;
  72. if(t[i].val>k) return rk(ls(i),k);
  73. if(t[i].val<k) return t[ls(i)].siz+t[i].cnt+rk(rs(i),k);
  74. return t[ls(i)].siz+1;
  75. }
  76. int kth(int i,int k){
  77. while(1){
  78. if(k<=t[ls(i)].siz) i=ls(i);
  79. else if(k>t[ls(i)].siz+t[i].cnt)
  80. k-=(t[ls(i)].siz+t[i].cnt),i=rs(i);
  81. else return t[i].val;
  82. }
  83. }
  84. int pre(int i,int k){
  85. if(!i) return -1e8;
  86. if(t[i].val>=k) return pre(ls(i),k);
  87. return max(pre(rs(i),k),t[i].val);
  88. }
  89. int nex(int i,int k){
  90. if(!i) return 1e8;
  91. if(t[i].val<=k) return nex(rs(i),k);
  92. return min(nex(ls(i),k),t[i].val);
  93. }
  94. } using namespace TREAP;
  95. signed main()
  96. {
  97. #ifndef ONLINE_JUDGE
  98. freopen("lty.in","r",stdin);
  99. freopen("lty.out","w",stdout);
  100. #endif
  101. m=read;
  102. int op,x;
  103. while(m-->0){
  104. op=read,x=read;
  105. switch(op){
  106. case 1:
  107. insert(rt,x);break;
  108. case 2:
  109. del(rt,x);break;
  110. case 3:
  111. write(rk(rt,x));pt;break;
  112. case 4:
  113. write(kth(rt,x));pt;break;
  114. case 5:
  115. write(pre(rt,x));pt;break;
  116. case 6:
  117. write(nex(rt,x));pt;break;
  118. }
  119. }
  120. return 0;
  121. }
FHQ_Treap
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define read read()
  4. #define pt puts("")
  5. inline int read{
  6. int x=0,f=1;char c=getchar();
  7. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  8. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  9. return f*x;
  10. }
  11. void write(int x){
  12. if(x<0) putchar('-'),x=-x;
  13. if(x>9) write(x/10);
  14. putchar(x%10+'0');
  15. return;
  16. }
  17. const int N=1e5+10;
  18. namespace FHQ_TREAP{
  19. struct Treap{
  20. int son[2],rd,cnt,siz,val;
  21. #define ls(i) t[i].son[0]
  22. #define rs(i) t[i].son[1]
  23. #define rd(i) t[i].rd
  24. #define cnt(i) t[i].cnt
  25. #define siz(i) t[i].siz
  26. #define val(i) t[i].val
  27. }t[N];
  28. int tot,rt;
  29. void up(int i){
  30. siz(i)=cnt(i)+siz(ls(i))+siz(rs(i));
  31. }
  32. int New(int k){
  33. val(++tot)=k;
  34. cnt(tot)=siz(tot)=1;
  35. rd(tot)=rand();
  36. return tot;
  37. }
  38. void split(int i,int k,int &x,int &y){
  39. if(!i){x=y=0;return;}
  40. if(val(i)>k) y=i,split(ls(i),k,x,ls(i));
  41. if(val(i)<=k) x=i,split(rs(i),k,rs(i),y);
  42. up(i);return;
  43. }
  44. void merge(int &i,int x,int y){
  45. if(!x||!y){i=x|y;return;}
  46. if(rd(x)>rd(y)) merge(rs(x),rs(x),y),i=x;
  47. else merge(ls(y),x,ls(y)),i=y;
  48. up(i);return;
  49. }
  50. void insert(int k){
  51. int rt1,rt2;
  52. split(rt,k-1,rt1,rt2);
  53. merge(rt,rt1,New(k));merge(rt,rt,rt2);
  54. return;
  55. }
  56. void del(int k){
  57. int rt1,rt2,cut;
  58. split(rt,k-1,rt1,rt2);split(rt2,k,cut,rt2);
  59. merge(cut,ls(cut),rs(cut));
  60. merge(rt,rt1,cut);merge(rt,rt,rt2);
  61. return;
  62. }
  63. int rk(int i,int k){
  64. int rt1,rt2,res;
  65. split(i,k-1,rt1,rt2);
  66. res=siz(rt1)+1;
  67. merge(i,rt1,rt2);
  68. return res;
  69. }
  70. int kth(int i,int k){
  71. while(1){
  72. if(k<=siz(ls(i))) i=ls(i);
  73. else if(k>siz(ls(i))+cnt(i))
  74. k-=(siz(ls(i))+cnt(i)),i=rs(i);
  75. else return val(i);
  76. }
  77. }
  78. int pre(int &i,int k){
  79. int rt1,rt2,res;
  80. split(i,k-1,rt1,rt2),res=rt1;
  81. while(rs(res)) res=rs(res);
  82. merge(i,rt1,rt2);
  83. return val(res);
  84. }
  85. int nxt(int &i,int k){
  86. int rt1,rt2,res;
  87. split(i,k,rt1,rt2),res=rt2;
  88. while(ls(res)) res=ls(res);
  89. merge(i,rt1,rt2);
  90. return val(res);
  91. }
  92. } using namespace FHQ_TREAP;
  93. int m;
  94. signed main()
  95. {
  96. #ifndef ONLINE_JUDGE
  97. freopen("lty.in","r",stdin);
  98. freopen("lty.out","w",stdout);
  99. #endif
  100. srand(time(0));
  101. m=read;
  102. int op,x;
  103. while(m-->0){
  104. op=read,x=read;
  105. switch(op){
  106. case 1:
  107. insert(x);
  108. break;
  109. case 2:
  110. del(x);
  111. break;
  112. case 3:
  113. write(rk(rt,x));pt;
  114. break;
  115. case 4:
  116. write(kth(rt,x));pt;
  117. break;
  118. case 5:
  119. write(pre(rt,x));pt;
  120. break;
  121. case 6:
  122. write(nxt(rt,x));pt;
  123. break;
  124. default:break;
  125. }
  126. }
  127. return 0;
  128. }
Splay
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define read read()
  4. #define pt puts("")
  5. inline int read{
  6. int x=0,f=1;char c=getchar();
  7. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  8. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  9. return f*x;
  10. }
  11. void write(int x){
  12. if(x<0) putchar('-'),x=-x;
  13. if(x>9) write(x/10);
  14. putchar(x%10+'0');
  15. return;
  16. }
  17. const int N = 1e5+10;
  18. int n;
  19. namespace SPLAY{
  20. struct Splay_Tree{
  21. int son[2],fa,cnt,siz,val;
  22. #define ls(i) t[i].son[0]
  23. #define rs(i) t[i].son[1]
  24. #define ds(i) t[i].son[d]
  25. #define bs(i) t[i].son[d^1]
  26. #define fa(i) t[i].fa
  27. #define cnt(i) t[i].cnt
  28. #define siz(i) t[i].siz
  29. #define val(i) t[i].val
  30. }t[N];
  31. int tot,rt;
  32. void up(int i){
  33. siz(i)=siz(ls(i))+siz(rs(i))+cnt(i);
  34. }
  35. void rotate(int x){
  36. int y=fa(x),z=fa(y);
  37. int d=(rs(y)==x);
  38. t[z].son[(rs(z)==y)]=x;fa(x)=z;
  39. ds(y)=bs(x);fa(bs(x))=y;
  40. bs(x)=y;fa(y)=x;
  41. up(y),up(x);
  42. }
  43. void splay(int x,int s){
  44. while(fa(x)!=s){
  45. int y=fa(x),z=fa(y);
  46. if(z!=s)
  47. (ls(y)==x)^(ls(z)==y)?rotate(x):rotate(y);
  48. rotate(x);
  49. }
  50. if(!s) rt=x;
  51. }
  52. void find(int k){
  53. if(!rt) return;
  54. int p=rt;
  55. while(t[p].son[val(p)<k]&&val(p)!=k){
  56. p=t[p].son[val(p)<k];
  57. }
  58. splay(p,0);
  59. }
  60. void insert(int k){
  61. int p=rt,f=0;
  62. while(p&&val(p)!=k){
  63. f=p;
  64. p=t[p].son[val(p)<k];
  65. }
  66. if(p) ++cnt(p);
  67. else{
  68. p=++tot;
  69. if(f) t[f].son[val(f)<k]=p;
  70. val(p)=k;fa(p)=f;
  71. siz(p)=cnt(p)=1;
  72. }
  73. splay(p,0);
  74. }
  75. int pre(int k){
  76. find(k);int p=rt;
  77. if(val(p)<k) return p;
  78. p=ls(p);while(rs(p)) p=rs(p);
  79. return p;
  80. }
  81. int nxt(int k){
  82. find(k);int p=rt;
  83. if(val(p)>k) return p;
  84. p=rs(p);while(ls(p)) p=ls(p);
  85. return p;
  86. }
  87. void del(int k){
  88. int prek=pre(k);
  89. int nxtk=nxt(k);
  90. splay(prek,0);splay(nxtk,prek);
  91. int cut=ls(nxtk);
  92. if(cnt(cut)>1)
  93. --cnt(cut),splay(cut,0);
  94. else ls(nxtk)=0;
  95. }
  96. int kth(int k){
  97. int i=rt;
  98. if(siz(i)<k) return 1;
  99. while(1){
  100. if(k<=siz(ls(i))) i=ls(i);
  101. else if(k>siz(ls(i))+cnt(i))
  102. k-=(siz(ls(i))+cnt(i)),i=rs(i);
  103. else return val(i);
  104. }
  105. }
  106. } using namespace SPLAY;
  107. signed main()
  108. {
  109. #ifndef ONLINE_JUDGE
  110. freopen("lty.in","r",stdin);
  111. freopen("lty.out","w",stdout);
  112. #endif
  113. n=read;
  114. insert(-1e8);insert(1e8);
  115. int op,x;
  116. for(int i=1;i<=n;i++){
  117. op=read,x=read;
  118. switch(op){
  119. case 1:
  120. insert(x);break;
  121. case 2:
  122. del(x);break;
  123. case 3:
  124. find(x);
  125. write(siz(ls(rt))),pt;break;
  126. case 4:
  127. write(kth(x+1)),pt;break;
  128. case 5:
  129. write(val(pre(x))),pt;break;
  130. case 6:
  131. write(val(nxt(x))),pt;break;
  132. default:
  133. break;
  134. }
  135. }
  136. return 0;
  137. }

\(T_A\) 营业额统计

板子,求前驱后继。

普通Treap
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define inf 1e10
  4. #define int long long
  5. #define read read()
  6. #define pt puts("")
  7. inline int read{
  8. int x=0,f=1;char c=getchar();
  9. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  10. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  11. return f*x;
  12. }
  13. void write(int x){
  14. if(x<0) putchar('-'),x=-x;
  15. if(x>9) write(x/10);
  16. putchar(x%10+'0');
  17. return;
  18. }
  19. const int N=(1<<15)+10;
  20. int n;
  21. int a;
  22. int ans;
  23. namespace TREAP{
  24. mt19937 Rand(0x7f);
  25. int tot,rt;
  26. struct Treap{
  27. int son[2],val,rd;
  28. #define ls(i) t[i].son[0]
  29. #define rs(i) t[i].son[1]
  30. #define val(i) t[i].val
  31. #define rd(i) t[i].rd
  32. }t[N];
  33. void rotate(int &i,int d){
  34. int s=t[i].son[d];
  35. t[i].son[d]=t[s].son[d^1];
  36. t[s].son[d^1]=i;
  37. i=s;
  38. return;
  39. }
  40. void insert(int &i,int k){
  41. if(!i){
  42. i=++tot;
  43. val(i)=k;rd(i)=Rand();
  44. return;
  45. }
  46. if(val(i)==k){
  47. return;
  48. }
  49. int d=(val(i)<k);
  50. insert(t[i].son[d],k);
  51. if(rd(i)>rd(t[i].son[d])) rotate(i,d);
  52. }
  53. int pre(int i,int k){
  54. if(!i) return -inf;
  55. if(val(i)>k) return pre(ls(i),k);
  56. return max(val(i),pre(rs(i),k));
  57. }
  58. int nxt(int i,int k){
  59. if(!i) return inf;
  60. if(val(i)<k) return nxt(rs(i),k);
  61. return min(val(i),nxt(ls(i),k));
  62. }
  63. } using namespace TREAP;
  64. signed main()
  65. {
  66. #ifndef ONLINE_JUDGE
  67. freopen("lty.in","r",stdin);
  68. freopen("lty.out","w",stdout);
  69. #endif
  70. n=read;
  71. a=read;
  72. ans=a;
  73. insert(rt,a);
  74. for(int i=2;i<=n;i++){
  75. a=read;
  76. int prea=pre(rt,a);
  77. int nxta=nxt(rt,a);
  78. ans+=min(a-prea,nxta-a);
  79. insert(rt,a);
  80. }
  81. write(ans);
  82. return 0;
  83. }
FHQ_Treap
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define int long long
  4. #define read read()
  5. #define pt puts("")
  6. inline int read{
  7. int x=0,f=1;char c=getchar();
  8. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  9. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  10. return f*x;
  11. }
  12. void write(int x){
  13. if(x<0) putchar('-'),x=-x;
  14. if(x>9) write(x/10);
  15. putchar(x%10+'0');
  16. return;
  17. }
  18. const int N = (1<<15)+10;
  19. namespace FHQ_TREAP{
  20. mt19937 Rand(0x7f);
  21. struct Treap{
  22. int son[2],rd,cnt,siz,val;
  23. #define ls(i) t[i].son[0]
  24. #define rs(i) t[i].son[1]
  25. #define ds(i) t[i].son[d]
  26. #define rd(i) t[i].rd
  27. #define cnt(i) t[i].cnt
  28. #define siz(i) t[i].siz
  29. #define val(i) t[i].val
  30. }t[N];
  31. int tot,rt;
  32. void up(int i){
  33. siz(i)=cnt(i)+siz(ls(i))+siz(rs(i));
  34. }
  35. int New(int k){
  36. val(++tot)=k;
  37. cnt(tot)=siz(tot)=1;
  38. rd(tot)=Rand();
  39. return tot;
  40. }
  41. void split(int i,int k,int &x,int &y){
  42. if(!i){x=y=0;return;}
  43. if(val(i)>k) y=i,split(ls(i),k,x,ls(i));
  44. if(val(i)<=k) x=i,split(rs(i),k,rs(i),y);
  45. up(i);
  46. }
  47. void merge(int &i,int x,int y){
  48. if(!x||!y){i=x|y;return;}
  49. if(rd(x)>rd(y)) merge(rs(x),rs(x),y),i=x;
  50. else merge(ls(y),x,ls(y)),i=y;
  51. up(i);
  52. }
  53. void insert(int k){
  54. int rt1,rt2;
  55. split(rt,k-1,rt1,rt2);
  56. merge(rt,rt1,New(k));
  57. merge(rt,rt,rt2);
  58. }
  59. int pre(int k){
  60. int rt1,rt2;
  61. split(rt,k,rt1,rt2);
  62. if(!siz(rt1)) return -1e8;
  63. int res=rt1;
  64. while(rs(res)) res=rs(res);
  65. merge(rt,rt1,rt2);
  66. return val(res);
  67. }
  68. int nxt(int k){
  69. int rt1,rt2;
  70. split(rt,k-1,rt1,rt2);
  71. if(!siz(rt2)) return 1e8;
  72. int res=rt2;
  73. while(ls(res)) res=ls(res);
  74. merge(rt,rt1,rt2);
  75. return val(res);
  76. }
  77. } using namespace FHQ_TREAP;
  78. int n,a;
  79. int ans;
  80. signed main()
  81. {
  82. #ifndef ONLINE_JUDGE
  83. freopen("lty.in","r",stdin);
  84. freopen("lty.out","w",stdout);
  85. #endif
  86. n=read;
  87. a=read;
  88. insert(a);
  89. ans=a;
  90. for(int i=2;i<=n;i++){
  91. a=read;
  92. int prea=pre(a);
  93. int nxta=nxt(a);
  94. ans+=min(a-prea,nxta-a);
  95. insert(a);
  96. }
  97. write(ans);
  98. return 0;
  99. }
Splay
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define read read()
  4. #define pt puts("")
  5. inline int read{
  6. int x=0,f=1;char c=getchar();
  7. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  8. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  9. return f*x;
  10. }
  11. void write(int x){
  12. if(x<0) putchar('-'),x=-x;
  13. if(x>9) write(x/10);
  14. putchar(x%10+'0');
  15. return;
  16. }
  17. const int N = 1e5+10;
  18. int n;
  19. namespace SPLAY{
  20. struct Splay_Tree{
  21. int son[2],fa,cnt,siz,val;
  22. #define ls(i) t[i].son[0]
  23. #define rs(i) t[i].son[1]
  24. #define ds(i) t[i].son[d]
  25. #define bs(i) t[i].son[d^1]
  26. #define fa(i) t[i].fa
  27. #define cnt(i) t[i].cnt
  28. #define siz(i) t[i].siz
  29. #define val(i) t[i].val
  30. }t[N];
  31. int tot,rt;
  32. void up(int i){
  33. siz(i)=siz(ls(i))+siz(rs(i))+cnt(i);
  34. }
  35. void rotate(int x){
  36. int y=fa(x),z=fa(y);
  37. int d=(rs(y)==x);
  38. t[z].son[(rs(z)==y)]=x;fa(x)=z;
  39. ds(y)=bs(x);fa(bs(x))=y;
  40. bs(x)=y;fa(y)=x;
  41. up(y),up(x);
  42. }
  43. void splay(int x,int s){
  44. while(fa(x)!=s){
  45. int y=fa(x),z=fa(y);
  46. if(z!=s)
  47. (ls(y)==x)^(ls(z)==y)?rotate(x):rotate(y);
  48. rotate(x);
  49. }
  50. if(!s) rt=x;
  51. }
  52. void find(int k){
  53. if(!rt) return;
  54. int p=rt;
  55. while(t[p].son[val(p)<k]&&val(p)!=k){
  56. p=t[p].son[val(p)<k];
  57. }
  58. splay(p,0);
  59. }
  60. void insert(int k){
  61. int p=rt,f=0;
  62. while(p&&val(p)!=k){
  63. f=p;
  64. p=t[p].son[val(p)<k];
  65. }
  66. if(p) ++cnt(p);
  67. else{
  68. p=++tot;
  69. if(f) t[f].son[val(f)<k]=p;
  70. val(p)=k;fa(p)=f;
  71. siz(p)=cnt(p)=1;
  72. }
  73. splay(p,0);
  74. }
  75. int pre(int k){
  76. find(k);int p=rt;
  77. if(val(p)<=k) return p;
  78. p=ls(p);while(rs(p)) p=rs(p);
  79. return p;
  80. }
  81. int nxt(int k){
  82. find(k);int p=rt;
  83. if(val(p)>=k) return p;
  84. p=rs(p);while(ls(p)) p=ls(p);
  85. return p;
  86. }
  87. } using namespace SPLAY;
  88. int a,ans;
  89. signed main()
  90. {
  91. #ifndef ONLINE_JUDGE
  92. freopen("lty.in","r",stdin);
  93. freopen("lty.out","w",stdout);
  94. #endif
  95. n=read;
  96. insert(-1e8);insert(1e8);
  97. a=read;
  98. ans=a;
  99. insert(a);
  100. for(int i=2;i<=n;i++){
  101. a=read;
  102. int prea=val(pre(a)),nxta=val(nxt(a));
  103. ans+=min(a-prea,nxta-a);
  104. insert(a);
  105. }
  106. write(ans);
  107. return 0;
  108. }

\(T_B\) 宠物收养所

发现某时刻的平衡树里只会全是人或者全是狗,查前驱后继即可,查完即删。

普通Treap
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define int long long
  4. #define inf 1e10
  5. #define read read()
  6. #define pt puts("")
  7. inline int read{
  8. int x=0,f=1;char c=getchar();
  9. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  10. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  11. return f*x;
  12. }
  13. void write(int x){
  14. if(x<0) putchar('-'),x=-x;
  15. if(x>9) write(x/10);
  16. putchar(x%10+'0');
  17. return;
  18. }
  19. const int N=8*1e4+10;
  20. const int p=1e6;
  21. int n;
  22. namespace TREAP{
  23. mt19937 Rand(0x7f);
  24. struct Treap{
  25. int son[2],cnt,siz,val,rd;
  26. #define ls(i) t[i].son[0]
  27. #define rs(i) t[i].son[1]
  28. #define cnt(i) t[i].cnt
  29. #define siz(i) t[i].siz
  30. #define val(i) t[i].val
  31. #define rd(i) t[i].rd
  32. }t[N];
  33. int tot,rt;
  34. void up(int i){
  35. siz(i)=siz(ls(i))+siz(rs(i))+cnt(i);
  36. }
  37. void rotate(int &i,int d){
  38. int s=t[i].son[d];
  39. t[i].son[d]=t[s].son[d^1];
  40. t[s].son[d^1]=i;
  41. up(i),i=s,up(i);
  42. return;
  43. }
  44. void insert(int &i,int k){
  45. if(!i){
  46. i=++tot;
  47. cnt(i)=siz(i)=1;
  48. val(i)=k;rd(i)=Rand();
  49. return;
  50. }
  51. siz(i)++;
  52. if(val(i)==k){
  53. cnt(i)++;return;
  54. }
  55. int d=(val(i)<k);
  56. insert(t[i].son[d],k);
  57. if(rd(i)>rd(t[i].son[d])) rotate(i,d);
  58. return;
  59. }
  60. void del(int &i,int k){
  61. if(!i) return;
  62. if(val(i)==k){
  63. if(cnt(i)>1){
  64. --cnt(i),--siz(i);
  65. return;
  66. }
  67. int d=(rd(ls(i))>rd(rs(i)));
  68. if(!ls(i)||!rs(i)) i=ls(i)+rs(i);
  69. else rotate(i,d),del(i,k);
  70. return;
  71. }
  72. int d=(val(i)<k);
  73. --siz(i);
  74. del(t[i].son[d],k);
  75. return;
  76. }
  77. int pre(int i,int k){
  78. if(!i) return -inf;
  79. if(val(i)>k) return pre(ls(i),k);
  80. return max(val(i),pre(rs(i),k));
  81. }
  82. int nxt(int i,int k){
  83. if(!i) return inf;
  84. if(val(i)<k) return nxt(rs(i),k);
  85. return min(val(i),nxt(ls(i),k));
  86. }
  87. } using namespace TREAP;
  88. int num[2];
  89. bool now,a;
  90. int b;
  91. int ans;
  92. signed main()
  93. {
  94. #ifndef ONLINE_JUDGE
  95. freopen("lty.in","r",stdin);
  96. freopen("lty.out","w",stdout);
  97. #endif
  98. n=read;
  99. now=read;
  100. num[now]++;
  101. b=read;insert(rt,b);
  102. for(int i=2;i<=n;i++){
  103. a=read,b=read;
  104. if(!num[a^1]){
  105. num[a]++;insert(rt,b);
  106. now=a;
  107. continue;
  108. }
  109. if(now^a){
  110. int preb=pre(rt,b);
  111. int nxtb=nxt(rt,b);
  112. int hwr=(b-preb<=nxtb-b?preb:nxtb);
  113. ans=(ans+abs(hwr-b))%p;
  114. del(rt,hwr);
  115. --num[now];
  116. }
  117. else
  118. insert(rt,b),++num[now];
  119. }
  120. write(ans);
  121. return 0;
  122. }
Splay
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define int long long
  4. #define inf 1e10
  5. #define read read()
  6. #define pt puts("")
  7. inline int read{
  8. int x=0,f=1;char c=getchar();
  9. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  10. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  11. return f*x;
  12. }
  13. void write(int x){
  14. if(x<0) putchar('-'),x=-x;
  15. if(x>9) write(x/10);
  16. putchar(x%10+'0');
  17. return;
  18. }
  19. const int N = 8*1e4+10;
  20. const int p = 1e6;
  21. int n;
  22. namespace SPLAY{
  23. struct Splay_Tree{
  24. int son[2],fa,cnt,siz,val;
  25. #define ls(i) t[i].son[0]
  26. #define rs(i) t[i].son[1]
  27. #define ds(i) t[i].son[d]
  28. #define bs(i) t[i].son[d^1]
  29. #define fa(i) t[i].fa
  30. #define cnt(i) t[i].cnt
  31. #define siz(i) t[i].siz
  32. #define val(i) t[i].val
  33. }t[N];
  34. int tot,rt;
  35. void up(int i){
  36. siz(i)=siz(ls(i))+siz(rs(i))+cnt(i);
  37. }
  38. void rotate(int x){
  39. int y=fa(x),z=fa(y);
  40. int d=(rs(y)==x);
  41. t[z].son[(rs(z)==y)]=x;fa(x)=z;
  42. ds(y)=bs(x);fa(bs(x))=y;
  43. bs(x)=y;fa(y)=x;
  44. up(y),up(x);
  45. }
  46. void splay(int x,int s){
  47. while(fa(x)!=s){
  48. int y=fa(x),z=fa(y);
  49. if(z!=s)
  50. (ls(y)==x)^(ls(z)==y)?rotate(x):rotate(y);
  51. rotate(x);
  52. }
  53. if(!s) rt=x;
  54. }
  55. void insert(int k){
  56. int p=rt,f=0;
  57. while(p && val(p)!=k){
  58. f=p;
  59. p=t[p].son[val(p)<k];
  60. }
  61. if(p) ++cnt(p);
  62. else{
  63. p=++tot;
  64. if(f) t[f].son[val(f)<k]=p;
  65. val(p)=k;fa(p)=f;
  66. siz(p)=cnt(p)=1;
  67. }
  68. splay(p,0);
  69. }
  70. void find(int k){
  71. if(!rt) return;
  72. int p=rt;
  73. while(t[p].son[val(p)<k] && val(p)!=k){
  74. p=t[p].son[val(p)<k];
  75. }
  76. splay(p,0);
  77. }
  78. int pre(int k,bool b){
  79. find(k);int p=rt;
  80. if(val(p)==k&&b) return p;
  81. if(val(p)<k) return p;
  82. p=ls(p);while(rs(p)) p=rs(p);
  83. return p;
  84. }
  85. int nxt(int k,bool b){
  86. find(k);int p=rt;
  87. if(val(p)==k&&b) return p;
  88. if(val(p)>k) return p;
  89. p=rs(p);while(ls(p)) p=ls(p);
  90. return p;
  91. }
  92. void del(int k){
  93. int prek=pre(k,0),nxtk=nxt(k,0);
  94. splay(prek,0);splay(nxtk,prek);
  95. int cut=ls(nxtk);
  96. if(cnt(cut)>1) --cnt(cut),splay(cut,0);
  97. else ls(nxtk)=0;
  98. }
  99. }
  100. using namespace SPLAY;
  101. bool now;
  102. int num[2];
  103. int a,b;
  104. int ans;
  105. signed main()
  106. {
  107. #ifndef ONLINE_JUDGE
  108. freopen("lty.in","r",stdin);
  109. freopen("lty.out","w",stdout);
  110. #endif
  111. insert(-inf);insert(inf);
  112. n=read;
  113. now=read;b=read;num[now]=1;
  114. insert(b);
  115. for(int i=2;i<=n;i++){
  116. a=read,b=read;
  117. if(!num[a^1]){
  118. ++num[a],now=a;
  119. insert(b);
  120. continue;
  121. }
  122. if(a==now){
  123. ++num[now];
  124. insert(b);
  125. }
  126. else{
  127. int preb=val(pre(b,1)),nxtb=val(nxt(b,1));
  128. int hwr=(b-preb<=nxtb-b?preb:nxtb);
  129. ans=(ans+abs(hwr-b))%p;
  130. del(hwr);
  131. --num[now];
  132. }
  133. }
  134. write(ans);
  135. return 0;
  136. }

注意 \(Splay\) 求前驱后继时如果要取等注意特判,删除时不可取等(取等就寄了)

\(T_C\) 郁闷的出纳员

维护整体懒标记,每次删除低于 \(minn-add\) 线的。

  • 用普通 \(\tt{Treap}\) 直接暴力删,不好打。
  • \(\tt{FHQ\_ Treap}\) 分裂出低于 \(minn-add\) 的部分,直接不要即可。

服了,\(\tt{FHQ\_ Treap}\) 跑不过普通 \(\tt{Treap}\) 的暴力。。。

普通Treap
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define read read()
  4. #define pt puts("")
  5. inline int read{
  6. int x=0,f=1;char c=getchar();
  7. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  8. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  9. return f*x;
  10. }
  11. void write(int x){
  12. if(x<0) putchar('-'),x=-x;
  13. if(x>9) write(x/10);
  14. putchar(x%10+'0');
  15. return;
  16. }
  17. const int N = 1e6;
  18. int n,minn;
  19. int add;
  20. int ans,sum;
  21. int num;
  22. namespace TREAP{
  23. mt19937 rnd(0x7f);
  24. struct Treap{
  25. int son[2],cnt,siz,val,rd;
  26. #define ls(i) t[i].son[0]
  27. #define rs(i) t[i].son[1]
  28. #define val(i) t[i].val
  29. #define cnt(i) t[i].cnt
  30. #define siz(i) t[i].siz
  31. }t[N];
  32. int tot,rt;
  33. void up(int i){
  34. t[i].siz=t[ls(i)].siz+t[rs(i)].siz+t[i].cnt;
  35. }
  36. void rotate(int &i,int d){
  37. int s=t[i].son[d];
  38. t[i].son[d]=t[s].son[d^1];
  39. t[s].son[d^1]=i;
  40. up(i),i=s,up(i);
  41. return;
  42. }
  43. void insert(int &i,int k){
  44. if(!i){
  45. i=++tot;
  46. t[i].cnt=t[i].siz=1;
  47. t[i].val=k,t[i].rd=rnd();
  48. return;
  49. }
  50. t[i].siz++;
  51. if(t[i].val==k){
  52. ++t[i].cnt;return;
  53. }
  54. int d=(t[i].val<k);
  55. insert(t[i].son[d],k);
  56. if(t[i].rd>t[t[i].son[d]].rd) rotate(i,d);
  57. return;
  58. }
  59. void del(int &i,int k){
  60. if(!i) return;
  61. if(t[i].val==k){
  62. if(t[i].cnt>1){
  63. --t[i].cnt,--t[i].siz;
  64. return;
  65. }
  66. int d=(t[ls(i)].rd>t[rs(i)].rd);
  67. if(!ls(i)||!rs(i)) i=ls(i)+rs(i);
  68. else rotate(i,d),del(i,k);
  69. return;
  70. }
  71. t[i].siz--;
  72. int d=(t[i].val<k);
  73. del(t[i].son[d],k);
  74. return;
  75. }
  76. int rk(int i,int k){
  77. if(!i) return 0;
  78. if(t[i].val>k) return rk(ls(i),k);
  79. if(t[i].val<k) return t[ls(i)].siz+t[i].cnt+rk(rs(i),k);
  80. return t[ls(i)].siz+1;
  81. }
  82. int find(int i,int k){
  83. while(1){
  84. if(k<=t[ls(i)].siz) i=ls(i);
  85. else if(k>t[ls(i)].siz+t[i].cnt)
  86. k-=(t[ls(i)].siz+t[i].cnt),i=rs(i);
  87. else return t[i].val;
  88. }
  89. }
  90. void dfs(int x){
  91. if(ls(x)) dfs(ls(x));
  92. if(rs(x)) dfs(rs(x));
  93. if(minn-add>val(x)){
  94. int c=cnt(x);
  95. for(int i=1;i<=c;i++)
  96. del(rt,val(x));
  97. }
  98. }
  99. } using namespace TREAP;
  100. signed main()
  101. {
  102. #ifndef ONLINE_JUDGE
  103. freopen("lty.in","r",stdin);
  104. freopen("lty.out","w",stdout);
  105. #endif
  106. n=read,minn=read;
  107. char op;
  108. int k;
  109. for(int i=1;i<=n;i++){
  110. cin>>op;k=read;
  111. switch (op){
  112. case 'I':
  113. if(k>=minn) insert(rt,k-add),++num;
  114. break;
  115. case 'A':
  116. add+=k;
  117. break;
  118. case 'S':
  119. add-=k;
  120. dfs(rt);
  121. break;
  122. case 'F':
  123. if(siz(rt)<k) ans=-1;
  124. else ans=find(rt,siz(rt)-k+1)+add;
  125. write(ans);pt;
  126. break;
  127. default:
  128. break;
  129. }
  130. }
  131. write(num-siz(rt));
  132. return 0;
  133. }
FHQ_Treap
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define read read()
  4. #define pt puts("")
  5. inline int read{
  6. int x=0,f=1;char c=getchar();
  7. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  8. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  9. return f*x;
  10. }
  11. void write(int x){
  12. if(x<0) putchar('-'),x=-x;
  13. if(x>9) write(x/10);
  14. putchar(x%10+'0');
  15. return;
  16. }
  17. const int N = 1e5+10;
  18. int n,minn,add;
  19. namespace FHQ_TREAP{
  20. mt19937 Rand(0x7f);
  21. struct Treap{
  22. int son[2],rd,cnt,siz,val;
  23. #define ls(i) t[i].son[0]
  24. #define rs(i) t[i].son[1]
  25. #define ds(i) t[i].son[d]
  26. #define rd(i) t[i].rd
  27. #define cnt(i) t[i].cnt
  28. #define siz(i) t[i].siz
  29. #define val(i) t[i].val
  30. }t[N];
  31. int tot,rt;
  32. void up(int i){
  33. siz(i)=siz(ls(i))+siz(rs(i))+cnt(i);
  34. }
  35. int New(int k){
  36. val(++tot)=k;
  37. siz(tot)=cnt(tot)=1;
  38. rd(tot)=Rand();
  39. return tot;
  40. }
  41. void spilt(int i,int k,int &x,int &y){
  42. if(!i){x=y=0;return;}
  43. if(val(i)>k) y=i,spilt(ls(i),k,x,ls(i));
  44. if(val(i)<=k) x=i,spilt(rs(i),k,rs(i),y);
  45. up(i);
  46. }
  47. void merge(int &i,int x,int y){
  48. if(!x||!y){i=x|y;return;}
  49. if(rd(x)>rd(y)) merge(rs(x),rs(x),y),i=x;
  50. else merge(ls(y),x,ls(y)),i=y;
  51. up(i);
  52. }
  53. void insert(int k){
  54. int rt1,rt2;
  55. spilt(rt,k-1,rt1,rt2);
  56. merge(rt,rt1,New(k));
  57. merge(rt,rt,rt2);
  58. return;
  59. }
  60. void del(int k){
  61. int rt1,rt2;
  62. spilt(rt,k-1,rt1,rt2);
  63. rt=rt2;
  64. }
  65. int kth(int i,int k){
  66. while(1){
  67. if(k<=siz(ls(i))) i=ls(i);
  68. else if(k>siz(ls(i))+cnt(i))
  69. k-=(siz(ls(i))+cnt(i)),i=rs(i);
  70. else return val(i);
  71. }
  72. }
  73. } using namespace FHQ_TREAP;
  74. int sum,ans;
  75. signed main()
  76. {
  77. #ifndef ONLINE_JUDGE
  78. freopen("lty.in","r",stdin);
  79. freopen("lty.out","w",stdout);
  80. #endif
  81. n=read,minn=read;
  82. char op;
  83. int k;
  84. for(int i=1;i<=n;i++){
  85. cin>>op;k=read;
  86. switch(op){
  87. case 'I':
  88. if(k>=minn)
  89. insert(k-add),++sum;
  90. break;
  91. case 'A':
  92. add+=k;
  93. break;
  94. case 'S':
  95. add-=k;
  96. del(minn-add);
  97. break;
  98. case 'F':
  99. if(siz(rt)<k) ans=-1;
  100. else ans=kth(rt,siz(rt)-k+1)+add;
  101. write(ans);pt;
  102. break;
  103. }
  104. }
  105. write(sum-siz(rt));
  106. return 0;
  107. }

\(T_E\) 文艺平衡树

平衡树不仅具有二叉搜索树的功能,同样可以支持区间操作,即,将序列的下标塞进平衡树,它的中序遍历就是原序列,然后我们想干嘛就干嘛~

对于区间翻转,考虑维护懒标记,下放时交换左右儿子,分别异或。最后中序遍历输出每个节点的值。

对于打懒标记,分裂出 \([l,r]\) 部分,一定要先分出前 \(r\) 个,再分前 \(l-1\) 个,反过来如果先分前 \(l-1\) 个,后面就应该分出 \(r-l+1\) 个,手画一下就知道为什么了。

这里使用 \(\tt{FHQ\_ Treap}\) 时,我们按子树大小进行分裂,因为我们是按照下标建的树,不能按权值分裂。

FHQ_Treap
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define swap(x,y) (x^=y,y^=x,x^=y)
  4. #define read read()
  5. #define pt puts("")
  6. inline int read{
  7. int x=0,f=1;char c=getchar();
  8. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  9. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  10. return f*x;
  11. }
  12. void write(int x){
  13. if(x<0) putchar('-'),x=-x;
  14. if(x>9) write(x/10);
  15. putchar(x%10+'0');
  16. return;
  17. }
  18. const int N = 1e5+10;
  19. int n,m;
  20. namespace FHQ_TREAP{
  21. struct Treap{
  22. int son[2],val,cnt,siz,rd,lazy;
  23. #define ls(i) t[i].son[0]
  24. #define rs(i) t[i].son[1]
  25. #define rd(i) t[i].rd
  26. #define cnt(i) t[i].cnt
  27. #define siz(i) t[i].siz
  28. #define val(i) t[i].val
  29. #define lazy(i) t[i].lazy
  30. }t[N];
  31. int tot,rt;
  32. int New(int k){
  33. val(++tot)=k;
  34. cnt(tot)=siz(tot)=1;
  35. rd(tot)=rand();
  36. return tot;
  37. }
  38. void up(int i){
  39. siz(i)=siz(ls(i))+siz(rs(i))+cnt(i);
  40. }
  41. void down(int i){
  42. if(!lazy(i)) return;
  43. swap(ls(i),rs(i));
  44. lazy(ls(i))^=1;
  45. lazy(rs(i))^=1;
  46. lazy(i)=0;
  47. }
  48. void split(int i,int k,int &x,int &y){
  49. if(!i){x=y=0;return;}
  50. down(i);
  51. if(siz(ls(i))+cnt(i)<=k) x=i,split(rs(i),k-(siz(ls(i))+cnt(i)),rs(i),y);
  52. else y=i,split(ls(i),k,x,ls(i));
  53. up(i);
  54. }
  55. void merge(int &i,int x,int y){
  56. if(!x||!y){i=x|y;return;}
  57. if(rd(x)>rd(y)) down(x),merge(rs(x),rs(x),y),i=x;
  58. else down(y),merge(ls(y),x,ls(y)),i=y;
  59. up(i);
  60. }
  61. void insert(int k){
  62. int rt1,rt2;
  63. split(rt,k,rt1,rt2);
  64. merge(rt,rt1,New(k));
  65. merge(rt,rt,rt2);
  66. }
  67. void out(int i){
  68. down(i);
  69. if(ls(i)) out(ls(i));
  70. write(val(i));putchar(' ');
  71. if(rs(i)) out(rs(i));
  72. }
  73. } using namespace FHQ_TREAP;
  74. signed main()
  75. {
  76. #ifndef ONLINE_JUDGE
  77. freopen("lty.in","r",stdin);
  78. freopen("lty.out","w",stdout);
  79. #endif
  80. srand(time(0));
  81. n=read,m=read;
  82. for(int i=1;i<=n;i++) insert(i);
  83. int l,r,rt1,rt2,rt3;
  84. for(int i=1;i<=m;i++){
  85. l=read,r=read;
  86. rt1=rt2=rt3=0;
  87. split(rt,r,rt1,rt3);
  88. split(rt1,l-1,rt1,rt2);
  89. lazy(rt2)^=1;
  90. merge(rt1,rt1,rt2);
  91. merge(rt,rt1,rt3);
  92. // split(rt,l-1,rt1,rt2);
  93. // split(rt2,r-l+1,rt2,rt3);
  94. // lazy(rt2)^=1;
  95. // merge(rt2,rt2,rt3);
  96. // merge(rt,rt1,rt2);
  97. }
  98. out(rt);
  99. return 0;
  100. }

对于 \(\tt{Splay}\),其实是差不多的,我们都是将区间转到一棵子树上进行打标记,类似于删除操作,我们将 \(l-1\) 转到根,将 \(r+1\) 转到根的儿子,那么 \(ls(rs(rt))\) 的子树就是区间 \([l,r]\),然后和 \(\tt{FHQ}\) 一样。

我的代码比较排斥 \(0\),所以我干脆将整体 \(+1\),最后答案 \(-1\) 输出。

Splay
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define swap(x,y) (x^=y,y^=x,x^=y)
  4. #define read read()
  5. #define pt puts("")
  6. inline int read{
  7. int x=0,f=1;char c=getchar();
  8. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  9. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  10. return f*x;
  11. }
  12. void write(int x){
  13. if(x<0) putchar('-'),x=-x;
  14. if(x>9) write(x/10);
  15. putchar(x%10+'0');
  16. return;
  17. }
  18. const int N = 1e5+10;
  19. int n,m;
  20. namespace SPLAY{
  21. struct Splay_Tree{
  22. int son[2],fa,lazy,siz,val;
  23. #define ls(i) t[i].son[0]
  24. #define rs(i) t[i].son[1]
  25. #define ds(i) t[i].son[d]
  26. #define bs(i) t[i].son[d^1]
  27. #define fa(i) t[i].fa
  28. #define lazy(i) t[i].lazy
  29. #define siz(i) t[i].siz
  30. #define val(i) t[i].val
  31. }t[N];
  32. int tot,rt;
  33. void up(int i){
  34. siz(i)=siz(ls(i))+siz(rs(i))+1;
  35. }
  36. void down(int i){
  37. if(!lazy(i)) return;
  38. lazy(i)=0;
  39. swap(ls(i),rs(i));
  40. lazy(ls(i))^=1,lazy(rs(i))^=1;
  41. }
  42. void rotate(int x){
  43. int y=fa(x),z=fa(y);
  44. int d=(rs(y)==x);
  45. t[z].son[rs(z)==y]=x;fa(x)=z;
  46. ds(y)=bs(x);fa(bs(x))=y;
  47. bs(x)=y;fa(y)=x;
  48. up(y),up(x);
  49. }
  50. void splay(int x,int s){
  51. while(fa(x)!=s){
  52. down(x);
  53. int y=fa(x),z=fa(y);
  54. if(z!=s)
  55. (ls(y)==x)^(ls(z)==y)?rotate(x):rotate(y);
  56. rotate(x);
  57. }
  58. if(!s) rt=x;
  59. }
  60. int find(int k){
  61. if(!rt) return 0;
  62. int p=rt;
  63. while(siz(ls(p))+1!=k){
  64. if(k<=siz(ls(p)))
  65. p=ls(p);
  66. else{
  67. k-=(siz(ls(p))+1);
  68. p=rs(p);
  69. }
  70. down(p);
  71. }
  72. return p;
  73. }
  74. void insert(int &i,int f,int x,int k){
  75. if(!i){
  76. i=++tot;
  77. siz(i)=1;fa(i)=f;val(i)=k;
  78. return;
  79. }
  80. if(x<=siz(ls(i))+1) insert(ls(i),i,x,k);
  81. else insert(rs(i),i,x-siz(ls(i))-1,k);
  82. up(i);
  83. }
  84. void out(int i){
  85. down(i);
  86. if(ls(i)) out(ls(i));
  87. if(val(i)>1&&val(i)<=n+1)
  88. write(val(i)-1),putchar(' ');
  89. if(rs(i)) out(rs(i));
  90. }
  91. } using namespace SPLAY;
  92. signed main()
  93. {
  94. #ifndef ONLINE_JUDGE
  95. freopen("lty.in","r",stdin);
  96. freopen("lty.out","w",stdout);
  97. #endif
  98. n=read,m=read;
  99. insert(rt,0,1,1);
  100. for(int i=1;i<=n;i++){
  101. insert(rt,0,i+1,i+1);
  102. splay(tot,0);
  103. }
  104. insert(rt,0,n+2,n+2);
  105. int l,r;
  106. for(int i=1;i<=m;i++){
  107. l=read+1,r=read+1;
  108. splay(find(l-1),0);
  109. splay(find(r+1),rt);
  110. int p=ls(rs(rt));
  111. lazy(p)^=1;
  112. }
  113. out(rt);
  114. return 0;
  115. }

\(T_F\) 二逼平衡树

线段树套平衡树板子。

  • 首先建立普通线段树,对于每个区间建一棵平衡树。
  • 对于 \(x\) 区间内排名,转化成查找区间内比它小的树的个数加 \(1\),分裂求。
  • 对于第 \(k\) 小数,考虑二分,通过操作 \(1\) 检查
  • 单点修改直接从根节点跑到叶子,路过的每个节点都要删掉原数,插入新数。注意要修改原序列。
  • 前驱后继直接分别查区间内每个小区间,取极值即可。

由于难写难调,只打了 FHQ_Treap

FHQ_Treap
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define read read()
  4. #define pt puts("")
  5. inline int read{
  6. int x=0,f=1;char c=getchar();
  7. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  8. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  9. return f*x;
  10. }
  11. void write(int x){
  12. if(x<0) putchar('-'),x=-x;
  13. if(x>9) write(x/10);
  14. putchar(x%10+'0');
  15. return;
  16. }
  17. const int N = 5*1e4+10;
  18. const int inf = 2147483647;
  19. int n,a[N],m;
  20. int MIN=inf,MAX=-inf;
  21. namespace FHQ_TREAP{
  22. struct Treap{
  23. int son[2],rd,cnt,siz,val;
  24. #define ls(i) t[i].son[0]
  25. #define rs(i) t[i].son[1]
  26. #define rd(i) t[i].rd
  27. #define cnt(i) t[i].cnt
  28. #define siz(i) t[i].siz
  29. #define val(i) t[i].val
  30. }t[N<<6];
  31. int tot;
  32. void up(int i){
  33. siz(i)=siz(ls(i))+siz(rs(i))+cnt(i);
  34. }
  35. int New(int k){
  36. val(++tot)=k;
  37. siz(tot)=cnt(tot)=1;
  38. rd(tot)=rand();
  39. return tot;
  40. }
  41. void split(int i,int k,int &x,int &y){
  42. if(!i){x=y=0;return;}
  43. if(val(i)<=k) x=i,split(rs(i),k,rs(i),y);
  44. else y=i,split(ls(i),k,x,ls(i));
  45. up(i);
  46. }
  47. void merge(int &i,int x,int y){
  48. if(!x||!y){i=x|y;return;}
  49. if(rd(x)>rd(y)) merge(rs(x),rs(x),y),i=x;
  50. else merge(ls(y),x,ls(y)),i=y;
  51. up(i);
  52. }
  53. void insert(int &rt,int k){
  54. int rt1,rt2;
  55. split(rt,k-1,rt1,rt2);
  56. merge(rt,rt1,New(k));
  57. merge(rt,rt,rt2);
  58. }
  59. void del(int &rt,int k){
  60. int rt1,rt2,cut;
  61. split(rt,k,rt1,rt2);
  62. split(rt1,k-1,rt1,cut);
  63. merge(cut,ls(cut),rs(cut));
  64. merge(rt1,rt1,cut);
  65. merge(rt,rt1,rt2);
  66. }
  67. int sumless(int rt,int k){
  68. int rt1,rt2;
  69. split(rt,k-1,rt1,rt2);
  70. int res=siz(rt1);
  71. merge(rt,rt1,rt2);
  72. return res;
  73. }
  74. int pre(int rt,int k){
  75. int rt1,rt2;
  76. split(rt,k-1,rt1,rt2);
  77. if(!siz(rt1)) return -inf;
  78. int res=rt1;
  79. while(rs(res)) res=rs(res);
  80. merge(rt,rt1,rt2);
  81. return val(res);
  82. }
  83. int nxt(int rt,int k){
  84. int rt1,rt2;
  85. split(rt,k,rt1,rt2);
  86. if(!siz(rt2)) return inf;
  87. int res=rt2;
  88. while(ls(res)) res=ls(res);
  89. merge(rt,rt1,rt2);
  90. return val(res);
  91. }
  92. #undef ls
  93. #undef rs
  94. };
  95. using namespace FHQ_TREAP;
  96. namespace Segment_Tree{
  97. struct SegTree{
  98. int l,r,rt;
  99. #define l(i) tr[i].l
  100. #define r(i) tr[i].r
  101. #define rt(i) tr[i].rt
  102. #define ls(i) (i<<1)
  103. #define rs(i) (i<<1|1)
  104. }tr[N<<2];
  105. void build(int i,int l,int r){
  106. l(i)=l,r(i)=r;
  107. for(int k=l;k<=r;k++){
  108. insert(rt(i),a[k]);
  109. }
  110. if(l==r) return;
  111. int mid=(l+r)>>1;
  112. build(ls(i),l,mid);
  113. build(rs(i),mid+1,r);
  114. }
  115. int lessk(int i,int ql,int qr,int k){
  116. int l=l(i),r=r(i);
  117. if(ql<=l&&r<=qr){
  118. return sumless(rt(i),k);
  119. }
  120. int mid=(l+r)>>1,res=0;
  121. if(ql<=mid) res+=lessk(ls(i),ql,qr,k);
  122. if(mid<qr) res+=lessk(rs(i),ql,qr,k);
  123. return res;
  124. }
  125. int q_rk(int ql,int qr,int k){
  126. return lessk(1,ql,qr,k)+1;
  127. }
  128. int q_kth(int ql,int qr,int k){
  129. int st=MIN,ed=MAX,res=0;
  130. while(st<=ed){
  131. int mid=(st+ed)>>1;
  132. if(q_rk(ql,qr,mid)<=k)
  133. res=mid,st=mid+1;
  134. else ed=mid-1;
  135. }
  136. return res;
  137. }
  138. void modify(int i,int x,int k){
  139. del(rt(i),a[x]);
  140. insert(rt(i),k);
  141. int l=l(i),r=r(i);
  142. if(l==r) return;
  143. int mid=(l+r)>>1;
  144. if(x<=mid) modify(ls(i),x,k);
  145. else modify(rs(i),x,k);
  146. }
  147. int q_pre(int i,int ql,int qr,int k){
  148. int l=l(i),r=r(i);
  149. if(ql<=l&&r<=qr){
  150. return pre(rt(i),k);
  151. }
  152. int mid=(l+r)>>1,res=-inf;
  153. if(ql<=mid) res=max(res,q_pre(ls(i),ql,qr,k));
  154. if(mid<qr) res=max(res,q_pre(rs(i),ql,qr,k));
  155. return res;
  156. }
  157. int q_nxt(int i,int ql,int qr,int k){
  158. int l=l(i),r=r(i);
  159. if(ql<=l&&r<=qr){
  160. return nxt(rt(i),k);
  161. }
  162. int mid=(l+r)>>1,res=inf;
  163. if(ql<=mid) res=min(res,q_nxt(ls(i),ql,qr,k));
  164. if(mid<qr) res=min(res,q_nxt(rs(i),ql,qr,k));
  165. return res;
  166. }
  167. };
  168. using namespace Segment_Tree;
  169. signed main()
  170. {
  171. srand(time(0));
  172. #ifndef ONLINE_JUDGE
  173. freopen("lty.in","r",stdin);
  174. freopen("lty.out","w",stdout);
  175. #endif
  176. n=read,m=read;
  177. for(int i=1;i<=n;i++) a[i]=read,MIN=min(MIN,a[i]),MAX=max(MAX,a[i]);
  178. build(1,1,n);
  179. int op,l,r,x;
  180. while(m-->0){
  181. op=read;
  182. switch(op){
  183. case 1:
  184. l=read,r=read,x=read;
  185. write(q_rk(l,r,x)),pt;break;
  186. case 2:
  187. l=read,r=read,x=read;
  188. write(q_kth(l,r,x)),pt;break;
  189. case 3:
  190. l=read,x=read;//不要忘记修改原序列
  191. modify(1,l,x);a[l]=x;break;
  192. case 4:
  193. l=read,r=read,x=read;
  194. write(q_pre(1,l,r,x)),pt;break;
  195. case 5:
  196. l=read,r=read,x=read;
  197. write(q_nxt(1,l,r,x)),pt;break;
  198. default:break;
  199. }
  200. }
  201. return 0;
  202. }

服了,洛谷数据太强大,我的常数也太强大,不得不写离散化。。

FHQ_Treap+离散化
  1. #include<bits/stdc++.h>
  2. #define getchar() getchar_unlocked()
  3. using namespace std;
  4. #define read read()
  5. #define pt puts("")
  6. inline int read{
  7. int x=0,f=1;char c=getchar();
  8. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  9. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  10. return f*x;
  11. }
  12. void write(int x){
  13. if(x<0) putchar('-'),x=-x;
  14. if(x>9) write(x/10);
  15. putchar(x%10+'0');
  16. return;
  17. }
  18. const int N = 5*1e4+10;
  19. const int inf = 2147483647;
  20. int n,a[N],m;
  21. int MIN=inf,MAX=-inf;
  22. int lsh[N<<1],num,tt;
  23. int op[N];
  24. int l[N],r[N],x[N];
  25. int ans[N],total;
  26. namespace FHQ_TREAP{
  27. struct Treap{
  28. int son[2],rd,cnt,siz,val;
  29. #define ls(i) t[i].son[0]
  30. #define rs(i) t[i].son[1]
  31. #define rd(i) t[i].rd
  32. #define cnt(i) t[i].cnt
  33. #define siz(i) t[i].siz
  34. #define val(i) t[i].val
  35. }t[N<<6];
  36. int tot;
  37. void up(int i){
  38. siz(i)=siz(ls(i))+siz(rs(i))+cnt(i);
  39. }
  40. int New(int k){
  41. val(++tot)=k;
  42. siz(tot)=cnt(tot)=1;
  43. rd(tot)=rand();
  44. return tot;
  45. }
  46. void split(int i,int k,int &x,int &y){
  47. if(!i){x=y=0;return;}
  48. if(val(i)<=k) x=i,split(rs(i),k,rs(i),y);
  49. else y=i,split(ls(i),k,x,ls(i));
  50. up(i);
  51. }
  52. void merge(int &i,int x,int y){
  53. if(!x||!y){i=x|y;return;}
  54. if(rd(x)>rd(y)) merge(rs(x),rs(x),y),i=x;
  55. else merge(ls(y),x,ls(y)),i=y;
  56. up(i);
  57. }
  58. void insert(int &rt,int k){
  59. int rt1,rt2;
  60. split(rt,k-1,rt1,rt2);
  61. merge(rt,rt1,New(k));
  62. merge(rt,rt,rt2);
  63. }
  64. void del(int &rt,int k){
  65. int rt1,rt2,cut;
  66. split(rt,k,rt1,rt2);
  67. split(rt1,k-1,rt1,cut);
  68. merge(cut,ls(cut),rs(cut));
  69. merge(rt1,rt1,cut);
  70. merge(rt,rt1,rt2);
  71. }
  72. int sumless(int rt,int k){
  73. int rt1,rt2;
  74. split(rt,k-1,rt1,rt2);
  75. int res=siz(rt1);
  76. merge(rt,rt1,rt2);
  77. return res;
  78. }
  79. int pre(int rt,int k){
  80. int rt1,rt2;
  81. split(rt,k-1,rt1,rt2);
  82. if(!siz(rt1)) return -inf;
  83. int res=rt1;
  84. while(rs(res)) res=rs(res);
  85. merge(rt,rt1,rt2);
  86. return val(res);
  87. }
  88. int nxt(int rt,int k){
  89. int rt1,rt2;
  90. split(rt,k,rt1,rt2);
  91. if(!siz(rt2)) return inf;
  92. int res=rt2;
  93. while(ls(res)) res=ls(res);
  94. merge(rt,rt1,rt2);
  95. return val(res);
  96. }
  97. #undef ls
  98. #undef rs
  99. };
  100. using namespace FHQ_TREAP;
  101. namespace Segment_Tree{
  102. struct SegTree{
  103. int l,r,rt;
  104. #define l(i) tr[i].l
  105. #define r(i) tr[i].r
  106. #define rt(i) tr[i].rt
  107. #define ls(i) (i<<1)
  108. #define rs(i) (i<<1|1)
  109. }tr[N<<2];
  110. void build(int i,int l,int r){
  111. l(i)=l,r(i)=r;
  112. for(int k=l;k<=r;k++){
  113. insert(rt(i),a[k]);
  114. }
  115. if(l==r) return;
  116. int mid=(l+r)>>1;
  117. build(ls(i),l,mid);
  118. build(rs(i),mid+1,r);
  119. }
  120. int lessk(int i,int ql,int qr,int k){
  121. int l=l(i),r=r(i);
  122. if(ql<=l&&r<=qr){
  123. return sumless(rt(i),k);
  124. }
  125. int mid=(l+r)>>1,res=0;
  126. if(ql<=mid) res+=lessk(ls(i),ql,qr,k);
  127. if(mid<qr) res+=lessk(rs(i),ql,qr,k);
  128. return res;
  129. }
  130. int q_rk(int ql,int qr,int k){
  131. return lessk(1,ql,qr,k)+1;
  132. }
  133. int q_kth(int ql,int qr,int k){
  134. int st=0,ed=num,res=0;
  135. while(st<=ed){
  136. int mid=(st+ed)>>1;
  137. if(q_rk(ql,qr,mid)<=k)
  138. res=mid,st=mid+1;
  139. else ed=mid-1;
  140. }
  141. return res;
  142. }
  143. void modify(int i,int x,int k){
  144. del(rt(i),a[x]);
  145. insert(rt(i),k);
  146. int l=l(i),r=r(i);
  147. if(l==r) return;
  148. int mid=(l+r)>>1;
  149. if(x<=mid) modify(ls(i),x,k);
  150. else modify(rs(i),x,k);
  151. }
  152. int q_pre(int i,int ql,int qr,int k){
  153. int l=l(i),r=r(i);
  154. if(ql<=l&&r<=qr){
  155. return pre(rt(i),k);
  156. }
  157. int mid=(l+r)>>1,res=-inf;
  158. if(ql<=mid) res=max(res,q_pre(ls(i),ql,qr,k));
  159. if(mid<qr) res=max(res,q_pre(rs(i),ql,qr,k));
  160. return res;
  161. }
  162. int q_nxt(int i,int ql,int qr,int k){
  163. int l=l(i),r=r(i);
  164. if(ql<=l&&r<=qr){
  165. return nxt(rt(i),k);
  166. }
  167. int mid=(l+r)>>1,res=inf;
  168. if(ql<=mid) res=min(res,q_nxt(ls(i),ql,qr,k));
  169. if(mid<qr) res=min(res,q_nxt(rs(i),ql,qr,k));
  170. return res;
  171. }
  172. };
  173. using namespace Segment_Tree;
  174. void LSH(){
  175. sort(lsh+1,lsh+tt+1);
  176. num=unique(lsh+1,lsh+tt+1)-lsh-1;
  177. for(int i=1;i<=n;i++){
  178. a[i]=lower_bound(lsh+1,lsh+num+1,a[i])-lsh;
  179. }
  180. }
  181. signed main()
  182. {
  183. srand(time(0));
  184. #ifndef ONLINE_JUDGE
  185. freopen("lty.in","r",stdin);
  186. freopen("lty.out","w",stdout);
  187. #endif
  188. n=read,m=read;
  189. for(int i=1;i<=n;i++) a[i]=lsh[++tt]=read;
  190. for(int i=1;i<=m;i++){
  191. op[i]=read;
  192. switch(op[i]){
  193. case 1:
  194. l[i]=read,r[i]=read,x[i]=read;break;
  195. case 2:
  196. l[i]=read,r[i]=read,x[i]=read;break;
  197. case 3:
  198. l[i]=read,x[i]=read;
  199. lsh[++tt]=x[i];break;
  200. case 4:
  201. l[i]=read,r[i]=read,x[i]=read;
  202. lsh[++tt]=x[i];break;
  203. case 5:
  204. l[i]=read,r[i]=read,x[i]=read;
  205. lsh[++tt]=x[i];break;
  206. default:break;
  207. }
  208. }
  209. LSH();
  210. build(1,1,n);
  211. for(int i=1;i<=m;i++){
  212. int an;
  213. switch(op[i]){
  214. case 1:
  215. x[i]=lower_bound(lsh+1,lsh+num+1,x[i])-lsh;
  216. ans[++total]=q_rk(l[i],r[i],x[i]);break;
  217. case 2:
  218. ans[++total]=lsh[q_kth(l[i],r[i],x[i])];break;
  219. case 3:
  220. x[i]=lower_bound(lsh+1,lsh+num+1,x[i])-lsh;
  221. modify(1,l[i],x[i]);a[l[i]]=x[i];break;
  222. case 4:
  223. x[i]=lower_bound(lsh+1,lsh+num+1,x[i])-lsh;
  224. an=q_pre(1,l[i],r[i],x[i]);
  225. ans[++total]=(an==-inf?-inf:lsh[an]);break;
  226. case 5:
  227. x[i]=lower_bound(lsh+1,lsh+num+1,x[i])-lsh;
  228. an=q_nxt(1,l[i],r[i],x[i]);
  229. ans[++total]=(an==inf?inf:lsh[an]);break;
  230. default:break;
  231. }
  232. }
  233. for(int i=1;i<=total;i++) write(ans[i]),pt;
  234. return 0;
  235. }

\(T_G\) JSOI2008火星人prefix

平衡树维护序列,\(\tt{FHQ}\) 按子树大小分裂,维护 \(hash\) 值,父节点存子树的 \(hash\) 值,最后二分求 \(LCP\)。注意插入字符后要 ++n

还是 \(\tt{FHQ}\) 好打,\(\tt{Splay}\) 以后再说。

FHQ_Treap
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define ull unsigned long long
  4. #define read read()
  5. #define pt puts("")
  6. #define gc getchar
  7. inline int read{
  8. int x=0,f=1;char c=getchar();
  9. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  10. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  11. return f*x;
  12. }
  13. void write(int x){
  14. if(x<0) putchar('-'),x=-x;
  15. if(x>9) write(x/10);
  16. putchar(x%10+'0');
  17. return;
  18. }
  19. #define N 100010
  20. const ull base = 233;
  21. int n,m;
  22. char s[N];
  23. ull pb[N];
  24. void init(){pb[0]=1ull;for(int i=1;i<N;i++)pb[i]=pb[i-1]*base;}
  25. namespace FHQ_Treap{
  26. struct Treap{
  27. int son[2],rd,siz,val;
  28. ull hash;
  29. #define ls(i) t[i].son[0]
  30. #define rs(i) t[i].son[1]
  31. #define rd(i) t[i].rd
  32. #define siz(i) t[i].siz
  33. #define val(i) t[i].val
  34. #define hash(i) t[i].hash
  35. }t[N<<1];
  36. int tot,rt;
  37. void up(int i){
  38. siz(i)=1+siz(ls(i))+siz(rs(i));
  39. hash(i)=hash(ls(i))*(pb[siz(rs(i))+1])+val(i)*pb[siz(rs(i))]+hash(rs(i));
  40. }
  41. int New(int k){
  42. val(++tot)=k;
  43. hash(tot)=k;
  44. siz(tot)=1;
  45. rd(tot)=rand();
  46. return tot;
  47. }
  48. void split(int i,int k,int &x,int &y){
  49. if(!i){x=y=0;return;}
  50. if(k<=siz(ls(i))) y=i,split(ls(i),k,x,ls(i));
  51. else x=i,split(rs(i),k-(siz(ls(i))+1),rs(i),y);
  52. up(i);
  53. }
  54. void merge(int &i,int x,int y){
  55. if(!x||!y){i=x|y;return;}
  56. if(rd(x)>rd(y)) merge(rs(x),rs(x),y),i=x;
  57. else merge(ls(y),x,ls(y)),i=y;
  58. up(i);
  59. }
  60. void insert(int x,int k){
  61. int rt1,rt2;
  62. split(rt,x,rt1,rt2);
  63. merge(rt,rt1,New(k));
  64. merge(rt,rt,rt2);
  65. }
  66. void replace(int x,int k){
  67. int rt1,rt2,rt3;
  68. split(rt,x,rt1,rt2);
  69. split(rt1,x-1,rt1,rt3);
  70. merge(rt1,rt1,New(k));
  71. merge(rt,rt1,rt2);
  72. }
  73. ull q_hash(int l,int r){
  74. int rt1,rt2,rt3;
  75. split(rt,r,rt2,rt3);
  76. split(rt2,l-1,rt1,rt2);
  77. ull res=hash(rt2);
  78. merge(rt2,rt1,rt2);
  79. merge(rt,rt2,rt3);
  80. return res;
  81. }
  82. } using namespace FHQ_Treap;
  83. int solve(int l,int r){
  84. int st=0,ed=n-r+1;
  85. int res=0;
  86. while(st<=ed){
  87. int mid=(st+ed)>>1;
  88. if(q_hash(l,l+mid-1)==q_hash(r,r+mid-1)){
  89. st=mid+1;res=mid;
  90. }
  91. else ed=mid-1;
  92. }
  93. return res;
  94. }
  95. signed main()
  96. {
  97. #ifndef ONLINE_JUDGE
  98. freopen("lty.in","r",stdin);
  99. freopen("lty.out","w",stdout);
  100. #endif
  101. scanf("%s",s+1);
  102. n=strlen(s+1);m=read;
  103. init();
  104. for(int i=1;i<=n;i++){
  105. insert(i-1,s[i]-'a'+1);
  106. }
  107. char op,x;
  108. int l,r;
  109. while(m-->0){
  110. op=gc();while(op!='Q'&&op!='R'&&op!='I')op=gc();
  111. switch(op){
  112. case 'Q':
  113. l=read,r=read;
  114. write(solve(l,r)),pt;
  115. break;
  116. case 'R':
  117. l=read;x=gc();while(x<'a'||x>'z') x=gc();
  118. replace(l,x-'a'+1);
  119. break;
  120. case 'I':
  121. l=read;x=gc();while(x<'a'||x>'z') x=gc();
  122. insert(l,x-'a'+1);++n;
  123. break;
  124. default:break;
  125. }
  126. }
  127. return 0;
  128. }

\(T_H\) 最长上升子序列

逆天性质题~~

本来对于 \(dp_i\) 表示以 \(i\) 位置结尾的最长上升子序列长度,有:

\[dp_i = \max_{1\leq j\leq i}^{a_j<a_i}dp_j + 1 \]

但是对于此题,他是按照 \(1\) ~ \(n\) 的顺序插入,也就是,当前插入的数,一定比原序列里所有数都大,那么

\[dp_i = \max_{1\leq j\leq i}dp_j + 1 \]

考虑平衡树维护序列,节点存子树里的 \(dp\) 最大值,直接转移即可。

  • 对于 \(\tt{FHQ}\),分裂出前 \(i\) 个,\(rt1\)\(dp\)\(\tt{+1}\) 即为所求
  • 对于 \(\tt{Splay}\),转到根节点,左儿子的 \(dp\)\(\tt{+1}\) 即为所求
FHQ_Treap
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define read read()
  4. #define pt puts("")
  5. inline int read{
  6. int x=0,f=1;char c=getchar();
  7. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  8. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  9. return f*x;
  10. }
  11. void write(int x){
  12. if(x<0) putchar('-'),x=-x;
  13. if(x>9) write(x/10);
  14. putchar(x%10+'0');
  15. return;
  16. }
  17. const int N = 1e5+10;
  18. int n;
  19. namespace FHQ_Treap{
  20. struct Treap{
  21. int son[2],rd,siz,len,ans;
  22. #define ls(i) t[i].son[0]
  23. #define rs(i) t[i].son[1]
  24. #define rd(i) t[i].rd
  25. #define siz(i) t[i].siz
  26. #define len(i) t[i].len
  27. #define ans(i) t[i].ans
  28. }t[N];
  29. int tot,rt;
  30. void up(int i){
  31. siz(i)=siz(ls(i))+siz(rs(i))+1;
  32. ans(i)=max(ans(ls(i)),ans(rs(i)));
  33. ans(i)=max(ans(i),len(i));
  34. }
  35. void split(int i,int k,int &x,int &y){
  36. if(!i){x=y=0;return;}
  37. if(k<=siz(ls(i))) y=i,split(ls(i),k,x,ls(i));
  38. else x=i,split(rs(i),k-(siz(ls(i))+1),rs(i),y);
  39. up(i);
  40. }
  41. void merge(int &i,int x,int y){
  42. if(!x||!y){i=x|y;return;}
  43. if(rd(x)>rd(y)) merge(rs(x),rs(x),y),i=x;
  44. else merge(ls(y),x,ls(y)),i=y;
  45. up(i);
  46. }
  47. int New(int k){
  48. ++tot;
  49. siz(tot)=1;
  50. ans(tot)=len(tot)=k;
  51. rd(tot)=rand();
  52. return tot;
  53. }
  54. void insert(int x){
  55. int rt1,rt2;
  56. split(rt,x,rt1,rt2);
  57. merge(rt,rt1,New(ans(rt1)+1));
  58. merge(rt,rt,rt2);
  59. }
  60. } using namespace FHQ_Treap;
  61. signed main()
  62. {
  63. #ifndef ONLINE_JUDGE
  64. freopen("lty.in","r",stdin);
  65. freopen("lty.out","w",stdout);
  66. #endif
  67. n=read;
  68. for(int x,i=1;i<=n;i++){
  69. x=read;
  70. insert(x);
  71. write(ans(rt));pt;
  72. }
  73. return 0;
  74. }
Splay
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define read read()
  4. #define pt puts("")
  5. inline int read{
  6. int x=0,f=1;char c=getchar();
  7. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  8. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
  9. return f*x;
  10. }
  11. void write(int x){
  12. if(x<0) putchar('-'),x=-x;
  13. if(x>9) write(x/10);
  14. putchar(x%10+'0');
  15. return;
  16. }
  17. const int N = 1e5+10;
  18. int n;
  19. namespace SPLAY{
  20. struct Splay_Tree{
  21. int son[2],fa,ans,siz,val;
  22. #define ls(i) t[i].son[0]
  23. #define rs(i) t[i].son[1]
  24. #define ds(i) t[i].son[d]
  25. #define bs(i) t[i].son[d^1]
  26. #define fa(i) t[i].fa
  27. #define siz(i) t[i].siz
  28. #define ans(i) t[i].ans
  29. #define val(i) t[i].val
  30. }t[N];
  31. int tot,rt;
  32. void up(int i){
  33. siz(i)=siz(ls(i))+siz(rs(i))+1;
  34. ans(i)=max(ans(ls(i)),ans(rs(i)));
  35. ans(i)=max(ans(i),val(i));
  36. }
  37. void rotate(int x){
  38. int y=fa(x),z=fa(y);
  39. int d=(rs(y)==x);
  40. t[z].son[(rs(z)==y)]=x;fa(x)=z;
  41. ds(y)=bs(x);fa(bs(x))=y;
  42. bs(x)=y;fa(y)=x;
  43. up(y),up(x);
  44. }
  45. void splay(int x,int s){
  46. while(fa(x)!=s){
  47. int y=fa(x),z=fa(y);
  48. if(z!=s)
  49. (ls(y)==x)^(ls(z)==y)?rotate(x):rotate(y);
  50. rotate(x);
  51. }
  52. if(!s) rt=x;
  53. }
  54. void find(int x){
  55. if(!rt) return;
  56. int p=rt;
  57. while(siz(ls(p))+1!=x){
  58. if(x<=siz(ls(p))+1){
  59. p=ls(p);
  60. }
  61. else{
  62. x-=(siz(ls(p))+1);
  63. p=rs(p);
  64. }
  65. }
  66. splay(p,0);
  67. }
  68. void insert(int &i,int f,int x,int k){
  69. if(!i){
  70. i=++tot;
  71. fa(i)=f;
  72. siz(i)=1;
  73. ans(i)=val(i)=k;
  74. return;
  75. }
  76. if(x<=siz(ls(i))+1) insert(ls(i),i,x,k);
  77. else insert(rs(i),i,x-siz(ls(i))-1,k);
  78. up(i);
  79. }
  80. } using namespace SPLAY;
  81. signed main()
  82. {
  83. #ifndef ONLINE_JUDGE
  84. freopen("lty.in","r",stdin);
  85. freopen("lty.out","w",stdout);
  86. #endif
  87. n=read;
  88. for(int x,k,i=1;i<=n;i++){
  89. x=read;
  90. if(!x) k=1;
  91. else if(x==i-1) k=ans(rt)+1;
  92. else{
  93. find(x+1);
  94. k=ans(ls(rt))+1;
  95. }
  96. insert(rt,0,x+1,k);
  97. splay(tot,0);
  98. write(ans(rt));pt;
  99. }
  100. return 0;
  101. }

\(T_I\) 星系探索

好像是阉割版的 \(\tt{ETT/LCT}\)(Wang54321说的),不会。

伍.闲话

转眼间在奥赛班的短短 \(3\) 个月只剩最后几天,整个下午跑机房,还能有多长时间。。。\(3\) 个月说长不长说短不短,学到了不少东西,虽然不像别的奥赛对高中文化课有很大帮助,但是:

我们学的东西是他们这辈子都不一定能接触到的。。。

不知道回原班在中考前还能学多长时间 \(\tt{OI}\),像小 \(\tt{H}\) 说的

也不知道回原班之后的二三十天,自己还能在这个机位上坐几个小时。这种感觉,或有点像心有余而力不足而被迫退役的感觉吧。当然我也希望,两年后的自己,不会有这种感觉。——\(\tt{HANGRY\_ Sol}\)

没想到二模完没有立刻【垃圾分类】,那就把 \(\tt{Splay}\) 收尾,不用放假加班了,珍惜机房的每一分钟吧...

原文链接:https://www.cnblogs.com/lty-ylzsx/p/18211987

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号