[BZ4923][Lydsy1706月赛]K小值查询
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下推。
注意事项
代码实现请注意:
- 删除任意一个非根节点之后,一定要更新他所有祖先的信息。
- 在向下遍历到一个新的节点/对子节点进行操作时,尽量随手Push_Down。(反正splay常数已经够大了QAQ)
- splay之前,请从根节点到splay点路径上的所有点Push_Down。(因为tag在旋转时会乱掉,而与size不同,tag无法在操作后用Push_Up操作从子节点还原父节点的tag
代码
/**************************************************************
Problem: 4923
User: GavinZheng
Language: C++
Result: Accepted
Time:9612 ms
Memory:5236 kb
****************************************************************/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define ll long long
#define inf (ll)(1e18+1000)
#define maxn (int)(1e5+1000)
using namespace std;
int idx,n,m,root,beh;
ll ma[maxn],val[maxn];
int tag[maxn],size[maxn],cnt[maxn],fa[maxn],son[maxn][2];
void push_down(int x){
if(!x)return;
if(son[x][0]){
val[son[x][0]]+=tag[x];
tag[son[x][0]]+=tag[x];
ma[son[x][0]]+=tag[x];
}
if(son[x][1]){
val[son[x][1]]+=tag[x];
tag[son[x][1]]+=tag[x];
ma[son[x][1]]+=tag[x];
}
tag[x]=0;
return;
}
void Down(int x){
if(!x)return;
Down(fa[x]);
push_down(x);
}
void push_up(int x){
size[x]=cnt[x];ma[x]=val[x];
if(son[x][0]){
size[x]+=size[son[x][0]];
ma[x]=max(ma[x],ma[son[x][0]]);
}
if(son[x][1]){
size[x]+=size[son[x][1]];
ma[x]=max(ma[x],ma[son[x][1]]);
}
return;
}
void rotate(int x){
int y=fa[x],z=fa[y],o=(son[y][1]==x);
son[y][o]=son[x][o^1];
fa[son[x][o^1]]=y;
son[x][o^1]=y;
fa[y]=x;
son[z][son[z][1]==y]=x;
fa[x]=z;
push_up(y);
push_up(x);
return;
}
void splay(int x,int v=0){//If x's father is v,stop rotating.
if(!x)return;
Down(x);
for(int y;(fa[x]&&fa[x]!=v);rotate(x)){
y=fa[x];
if(fa[y]==v)continue;
rotate((son[fa[y]][0]==y)==(son[fa[x]][0]==x)?y:x);
}
if(!v)root=x;
}
void insert(int x,ll a,int cntx=1,int idxx=0){
int y=0;
while(x&&a!=val[x]){
push_down(x);
y=x;
x=son[x][a>val[x]];
}
if(x){
cnt[x]+=cntx;splay(x);
}
else{
x=(idxx?idxx:++idx);
val[x]=a;
fa[x]=y;
son[y][a>val[y]]=x;
cnt[x]=cntx;
tag[x]=0;
son[x][0]=son[x][1]=0;
push_up(x);splay(x);
}
return;
}
int kth(int x,int k){
push_down(x);
if(size[son[x][0]]>=k) return kth(son[x][0],k);
if(size[son[x][0]]+cnt[x]>=k) return x;
else return kth(son[x][1],k-size[son[x][0]]-cnt[x]);
}
void suc(int x,int a){
while(x){
push_down(x);
if(val[x]>a){
beh=x;
x=son[x][0];
}
else{
x=son[x][1];
}
}
return;
}
void reinsert(int x,int k){
if(!x)return;
push_down(x);
if(son[x][0])reinsert(son[x][0],k);
if(son[x][1])reinsert(son[x][1],k);
insert(root,val[x]-k,cnt[x],x);
return;
}
void change(int k){
suc(root,2*k);
int k2_mark=beh;
suc(root,k);
int k_mark=beh;
splay(k2_mark);
val[root]-=k;val[son[root][1]]-=k;ma[son[root][1]]-=k;tag[son[root][1]]-=k;
push_down(son[root][1]);
push_up(root);
splay(k_mark);
splay(kth(root,size[son[k_mark][0]]));//put the previous of k on the root_position
//if k presense in the tree,put k instead.
splay(k2_mark,root);
push_down(root);push_down(son[root][1]);
int head=son[son[root][1]][0];son[son[root][1]][0]=0;fa[head]=0;
push_up(son[root][1]);push_up(root);reinsert(head,k);
}
int main(){
//freopen("in","r",stdin);
scanf("%d%d",&n,&m);
insert(root,inf);insert(root,-inf);
for(int i=1;i<=n;i++){
int a;scanf("%d",&a);insert(root,a);
}
for(int i=1;i<=m;i++){
int type,num;scanf("%d%d",&type,&num);
if(type==1) printf("%lld\n",val[kth(root,num+1)]);
else change(num);
}
}
[BZ4923][Lydsy1706月赛]K小值查询的更多相关文章
- BZOJ4923:[Lydsy1706月赛]K小值查询(Splay)
Description 维护一个长度为n的正整数序列a_1,a_2,...,a_n,支持以下两种操作: 1 k,将序列a从小到大排序,输出a_k的值. 2 k,将所有严格大于k的数a_i减去k. In ...
- 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 ...
- 4923: [Lydsy1706月赛]K小值查询 平衡树 非旋转Treap
国际惯例的题面:这种维护排序序列,严格大于的进行操作的题都很套路......我们按照[0,k],(k,2k],(2k,inf)分类讨论一下就好.显然第一个区间的不会变化,第二个区间的会被平移进第一个区 ...
- BZOJ4923 [Lydsy1706月赛]K小值查询
题意 维护一个长度为n的正整数序列a_1,a_2,...,a_n,支持以下两种操作: 1 k,将序列a从小到大排序,输出a_k的值. 2 k,将所有严格大于k的数a_i减去k. \(n \leq 10 ...
- [BZOJ 4923][Lydsy1706月赛]K小值查询
传送门 势能分析平衡树,splay或treap都可以 放个指针版的就跑 #include <bits/stdc++.h> using namespace std; #define rep( ...
- [bzoj4923]K小值查询
来自FallDream的博客,未经允许,请勿转载,谢谢. 维护一个长度为n的正整数序列a_1,a_2,...,a_n,支持以下两种操作: 1 k,将序列a从小到大排序,输出a_k的值. 2 k,将所有 ...
- BZOJ4923 K小值查询(splay)
容易想到建一棵平衡树,修改时打上标记即可.但是修改会导致平衡树结构被破坏.注意到实际上只有[k+1,2k)这一部分数在平衡树中的位置会被改变,所以对这一部分暴力修改,因为每次都会使其至少减小一半,复杂 ...
- 【BZOJ】3065: 带插入区间K小值
http://www.lydsy.com/JudgeOnline/problem.php?id=3065 题意:带插入.修改的区间k小值在线查询.(原序列n<=35000, 询问<=175 ...
- bzoj 3065: 带插入区间K小值 替罪羊树 && AC300
3065: 带插入区间K小值 Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 1062 Solved: 253[Submit][Status] Des ...
随机推荐
- CSAPP深入理解计算机系统(第二版)第三章家庭作业答案
<深入理解计算机系统(第二版)>CSAPP 第三章 家庭作业 这一章介绍了AT&T的汇编指令 比较重要 本人完成了<深入理解计算机系统(第二版)>(以下简称CSAPP) ...
- gdb解决字符串打印果断措施
在我们进行gdb动态调试的时候,很多时间可能会遇到无法完全显示的情况 关于这种方法网上已经有解决方法 https://blog.csdn.net/shuizhizhiyin/article/detai ...
- randint模块无法加载
原因:把程序文件名命名为 random.py 导致 random.randint 加载的是这个文件的模块
- Android应用程序类型和进程状态
来自<Android4高级编程> Android应用程序不能控制自己的生命周期,应用程序组件(Activity.Service等其他组件)必须监听应用程序状态的变化并做出适当的反应,而且特 ...
- Leetcode 记录(201~300)
实习面试前再完成100题,争取能匀速解释清楚题 204. Count Primes 素数筛 class Solution { public: int countPrimes(int n) { ) ; ...
- 基于.net的微服务架构下的开发测试环境运维实践
眼下,做互联网应用,最火的架构是微服务,最热的研发管理就是DevOps, 没有之一.微服务.DevOps已经被大量应用,它们已经像传说中的那样,可以无所不能.特来电云平台,通过近两年多的实践,发现完全 ...
- 文件上传(xls)
function UploadFile(){ var filewj =document.getElementById("filewj").files[0]; //input Id ...
- React(六)Props属性
state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变.这就是为什么有些容器组件需要定义 state 来更新和修改数据. 而子组件只能通过 pro ...
- Codechef August Challenge 2018 : Chef at the River
传送门 (要是没有tjm(Sakits)的帮忙,我还真不知道啥时候能做出来 结论是第一次带走尽可能少的动物,使未带走的动物不冲突,带走的这个数量就是最优解. 首先这个数量肯定是下界,更少的话连第一次都 ...
- vue学习:vue+webpack的快速使用指南(新手向)
一.vue有两种使用方式: 1.下载vue.js <script src="vue.js"></script> 2.使用npm npm install vu ...