K小值查询

题面

维护一个长度为n的正整数序列a_1,a_2,...,a_n,支持以下两种操作:

1 k,将序列a从小到大排序,输出a_k的值。

2 k,将所有严格大于k的数a_i减去k。

Input

第一行包含两个正整数n,m(1<=n,m<=100000),分别表示序列的长度和操作的个数。

第二行包含n个正整数a_1,a_2,...,a_n(1<=a_i<=10^9),分别表示序列中的每个元素。

接下来m行,每行两个正整数op(1<=op<=2),k,若op=1,则1<=k<=n;若op=2,则1<=k<=10^9;依次描述每个操作。

Output

输出若干行,对于每个询问输出一行一个整数,即第k小的值。

Sample Input

4 5

1 5 6 12

2 5

1 1

1 2

1 3

1 4

Sample Output

1

1

5

7

思路

乍一看是一道平衡树上的区间修改。然而发现操作1是排名操作。显然,区间修改的splay是以编号作为关键字维护的,无法排名。那我们就思考如何用权值关键字的splay解决问题。

我们能发现:所有严格大于k的节点减去k之后相对大小都不会改变。而一颗子平衡树是按照内部元素大小关系排序的。所以我们推出:对于所有节点权值都严格大于k的子树,全部减k后树的形状不会改变。

我们可以先把所有点根据这个特性分成三个区间:\((-inf,k],(k,2k],(2k,+inf)\)。我们容易发现,每次操作后两个区间所有数都会减去k,操作后第二个区间和第一个区间会合并。而第三个区间一部分会成为新的2区间,一部分会留在3区间。

我们考虑修改操作,先把k的后继的前驱(注意,这个点不是k,因为k可能不在树里)转到root,再把2k的后继转到k的右儿子。然后把2k这个节点的左子树从主树上拆下来,则得到代表区间\((k,2k]\)的子树。然后把这些节点拆散,暴力的把每个节点的val减去k,然后再一个一个暴力插入。因为所有数在区间2最多存在一次,所以这一步复杂度为\(O(n \space log\space n)\)。第三个区间则将2k节点,打个tag即可。

查询操作还是常规的查询操作,只是每次递归到一个点后先执行Push_Down操作,即把tag下推。

注意事项

代码实现请注意:

  1. 删除任意一个非根节点之后,一定要更新他所有祖先的信息。
  2. 在向下遍历到一个新的节点/对子节点进行操作时,尽量随手Push_Down。(反正splay常数已经够大了QAQ)
  3. splay之前,请从根节点到splay点路径上的所有点Push_Down。(因为tag在旋转时会乱掉,而与size不同,tag无法在操作后用Push_Up操作从子节点还原父节点的tag

代码

  1. /**************************************************************
  2. Problem: 4923
  3. User: GavinZheng
  4. Language: C++
  5. Result: Accepted
  6. Time:9612 ms
  7. Memory:5236 kb
  8. ****************************************************************/
  9. #include<iostream>
  10. #include<cstdio>
  11. #include<algorithm>
  12. #include<cmath>
  13. #define ll long long
  14. #define inf (ll)(1e18+1000)
  15. #define maxn (int)(1e5+1000)
  16. using namespace std;
  17. int idx,n,m,root,beh;
  18. ll ma[maxn],val[maxn];
  19. int tag[maxn],size[maxn],cnt[maxn],fa[maxn],son[maxn][2];
  20. void push_down(int x){
  21. if(!x)return;
  22. if(son[x][0]){
  23. val[son[x][0]]+=tag[x];
  24. tag[son[x][0]]+=tag[x];
  25. ma[son[x][0]]+=tag[x];
  26. }
  27. if(son[x][1]){
  28. val[son[x][1]]+=tag[x];
  29. tag[son[x][1]]+=tag[x];
  30. ma[son[x][1]]+=tag[x];
  31. }
  32. tag[x]=0;
  33. return;
  34. }
  35. void Down(int x){
  36. if(!x)return;
  37. Down(fa[x]);
  38. push_down(x);
  39. }
  40. void push_up(int x){
  41. size[x]=cnt[x];ma[x]=val[x];
  42. if(son[x][0]){
  43. size[x]+=size[son[x][0]];
  44. ma[x]=max(ma[x],ma[son[x][0]]);
  45. }
  46. if(son[x][1]){
  47. size[x]+=size[son[x][1]];
  48. ma[x]=max(ma[x],ma[son[x][1]]);
  49. }
  50. return;
  51. }
  52. void rotate(int x){
  53. int y=fa[x],z=fa[y],o=(son[y][1]==x);
  54. son[y][o]=son[x][o^1];
  55. fa[son[x][o^1]]=y;
  56. son[x][o^1]=y;
  57. fa[y]=x;
  58. son[z][son[z][1]==y]=x;
  59. fa[x]=z;
  60. push_up(y);
  61. push_up(x);
  62. return;
  63. }
  64. void splay(int x,int v=0){//If x's father is v,stop rotating.
  65. if(!x)return;
  66. Down(x);
  67. for(int y;(fa[x]&&fa[x]!=v);rotate(x)){
  68. y=fa[x];
  69. if(fa[y]==v)continue;
  70. rotate((son[fa[y]][0]==y)==(son[fa[x]][0]==x)?y:x);
  71. }
  72. if(!v)root=x;
  73. }
  74. void insert(int x,ll a,int cntx=1,int idxx=0){
  75. int y=0;
  76. while(x&&a!=val[x]){
  77. push_down(x);
  78. y=x;
  79. x=son[x][a>val[x]];
  80. }
  81. if(x){
  82. cnt[x]+=cntx;splay(x);
  83. }
  84. else{
  85. x=(idxx?idxx:++idx);
  86. val[x]=a;
  87. fa[x]=y;
  88. son[y][a>val[y]]=x;
  89. cnt[x]=cntx;
  90. tag[x]=0;
  91. son[x][0]=son[x][1]=0;
  92. push_up(x);splay(x);
  93. }
  94. return;
  95. }
  96. int kth(int x,int k){
  97. push_down(x);
  98. if(size[son[x][0]]>=k) return kth(son[x][0],k);
  99. if(size[son[x][0]]+cnt[x]>=k) return x;
  100. else return kth(son[x][1],k-size[son[x][0]]-cnt[x]);
  101. }
  102. void suc(int x,int a){
  103. while(x){
  104. push_down(x);
  105. if(val[x]>a){
  106. beh=x;
  107. x=son[x][0];
  108. }
  109. else{
  110. x=son[x][1];
  111. }
  112. }
  113. return;
  114. }
  115. void reinsert(int x,int k){
  116. if(!x)return;
  117. push_down(x);
  118. if(son[x][0])reinsert(son[x][0],k);
  119. if(son[x][1])reinsert(son[x][1],k);
  120. insert(root,val[x]-k,cnt[x],x);
  121. return;
  122. }
  123. void change(int k){
  124. suc(root,2*k);
  125. int k2_mark=beh;
  126. suc(root,k);
  127. int k_mark=beh;
  128. splay(k2_mark);
  129. val[root]-=k;val[son[root][1]]-=k;ma[son[root][1]]-=k;tag[son[root][1]]-=k;
  130. push_down(son[root][1]);
  131. push_up(root);
  132. splay(k_mark);
  133. splay(kth(root,size[son[k_mark][0]]));//put the previous of k on the root_position
  134. //if k presense in the tree,put k instead.
  135. splay(k2_mark,root);
  136. push_down(root);push_down(son[root][1]);
  137. int head=son[son[root][1]][0];son[son[root][1]][0]=0;fa[head]=0;
  138. push_up(son[root][1]);push_up(root);reinsert(head,k);
  139. }
  140. int main(){
  141. //freopen("in","r",stdin);
  142. scanf("%d%d",&n,&m);
  143. insert(root,inf);insert(root,-inf);
  144. for(int i=1;i<=n;i++){
  145. int a;scanf("%d",&a);insert(root,a);
  146. }
  147. for(int i=1;i<=m;i++){
  148. int type,num;scanf("%d%d",&type,&num);
  149. if(type==1) printf("%lld\n",val[kth(root,num+1)]);
  150. else change(num);
  151. }
  152. }

[BZ4923][Lydsy1706月赛]K小值查询的更多相关文章

  1. BZOJ4923:[Lydsy1706月赛]K小值查询(Splay)

    Description 维护一个长度为n的正整数序列a_1,a_2,...,a_n,支持以下两种操作: 1 k,将序列a从小到大排序,输出a_k的值. 2 k,将所有严格大于k的数a_i减去k. In ...

  2. BZOJ 4923: [Lydsy1706月赛]K小值查询 Splay + 思维

    Description 维护一个长度为n的正整数序列a_1,a_2,...,a_n,支持以下两种操作: 1 k,将序列a从小到大排序,输出a_k的值. 2 k,将所有严格大于k的数a_i减去k. In ...

  3. 4923: [Lydsy1706月赛]K小值查询 平衡树 非旋转Treap

    国际惯例的题面:这种维护排序序列,严格大于的进行操作的题都很套路......我们按照[0,k],(k,2k],(2k,inf)分类讨论一下就好.显然第一个区间的不会变化,第二个区间的会被平移进第一个区 ...

  4. BZOJ4923 [Lydsy1706月赛]K小值查询

    题意 维护一个长度为n的正整数序列a_1,a_2,...,a_n,支持以下两种操作: 1 k,将序列a从小到大排序,输出a_k的值. 2 k,将所有严格大于k的数a_i减去k. \(n \leq 10 ...

  5. [BZOJ 4923][Lydsy1706月赛]K小值查询

    传送门 势能分析平衡树,splay或treap都可以 放个指针版的就跑 #include <bits/stdc++.h> using namespace std; #define rep( ...

  6. [bzoj4923]K小值查询

    来自FallDream的博客,未经允许,请勿转载,谢谢. 维护一个长度为n的正整数序列a_1,a_2,...,a_n,支持以下两种操作: 1 k,将序列a从小到大排序,输出a_k的值. 2 k,将所有 ...

  7. BZOJ4923 K小值查询(splay)

    容易想到建一棵平衡树,修改时打上标记即可.但是修改会导致平衡树结构被破坏.注意到实际上只有[k+1,2k)这一部分数在平衡树中的位置会被改变,所以对这一部分暴力修改,因为每次都会使其至少减小一半,复杂 ...

  8. 【BZOJ】3065: 带插入区间K小值

    http://www.lydsy.com/JudgeOnline/problem.php?id=3065 题意:带插入.修改的区间k小值在线查询.(原序列n<=35000, 询问<=175 ...

  9. bzoj 3065: 带插入区间K小值 替罪羊树 && AC300

    3065: 带插入区间K小值 Time Limit: 60 Sec  Memory Limit: 512 MBSubmit: 1062  Solved: 253[Submit][Status] Des ...

随机推荐

  1. 一天带你入门到放弃vue.js(一)

    写在前面的话! 每个新的框架入手都会进行一些列的扯犊子!这里不多说那么多!简简单单说一下vue吧! Vue.js是目前三大框架(angular,vue,react)之一,是渐进式js框架,据说是摒弃了 ...

  2. nodejs+koa在header里面添加header信息

    参考:https://koa.bootcss.com/ ctx.append('resultCode', '0000'); ctx.append('resultMessage', 'success') ...

  3. Telephone Lines [POJ3662] [二分答案]

    Description Farmer John打算将电话线引到自己的农场,但电信公司并不打算为他提供免费服务.于是,FJ必须为此向电信公司支付一定的费用. FJ的农场周围分布着N(1 <= N ...

  4. Ubuntu 安装 Redis和phpredis扩展

    服务器Ubuntu16.04 环境php7.0+Apache /****************************开始安装Redis****************************/ 1 ...

  5. Git&Version Control

    Git Git(读音为/gɪt/.)是一个开源的分布式版本控制系统,可以有效.高速地处理从很小到非常大的项目版本管理. [1]  Git 是 Linus Torvalds 为了帮助管理 Linux 内 ...

  6. oracle中查询用户信息

    1.查看所有用户: select * from dba_users; select * from all_users; select * from user_users; 2.查看用户或角色系统权限( ...

  7. Windows 安装JDK

    Windows 安装JDK jdk为java开发工具,jre为java运行环境,安装一个jdk版本会把两个一起装 步骤: 1.在官网下载jdk:http://www.oracle.com/techne ...

  8. js 利用jquery.gridly.js实现拖拽并且排序

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. Express全系列教程之(八):session的基本使用

    一.关于session session是另一种记录客户状态的机制,与cookie保存在客户端浏览器不同,session保存在服务器当中:当客户端访问服务器时,服务器会生成一个session对象,对象中 ...

  10. 【转载】Python日期时间模块datetime详解与Python 日期时间的比较,计算实例代码

    本文转载自脚本之家,源网址为:https://www.jb51.net/article/147429.htm 一.Python中日期时间模块datetime介绍 (一).datetime模块中包含如下 ...