1829. [Tyvj 1728]普通平衡树
★★★ 输入文件:phs.in
输出文件:phs.out
简单对比
时间限制:1 s 内存限制:1000 MB
【题目描述】
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
【输入格式】
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
【输出格式】
对于操作3,4,5,6每行输出一个数,表示对应答案
【样例输入】
10 1 106465 4 1 1 317721 1 460929 1 644985 1 84185 1 89851 6 81968 1 492737 5 493598
【样例输出】
106465 84185 492737
【提示】
#include<bits/stdc++.h> #define maxn 100005 #define maxSIZE maxn*20 #define INF 1e7 #define mid (l+r>>1) using namespace std; int sum[maxSIZE],ls[maxSIZE],rs[maxSIZE]; int RT,cnt;//动态开点 不会炸内存 void Add(int &rt,int l,int r,int pos,int qx) { if(!rt) rt=++cnt; if(l==r) { sum[rt]+=qx; return; } if(pos<=mid) Add(ls[rt],l,mid,pos,qx); else Add(rs[rt],mid+1,r,pos,qx); sum[rt]=sum[ls[rt]]+sum[rs[rt]]; return; } int Sum(int rt,int l,int r,int s,int t) { if(!rt||s>r||t<l) return 0; if(s<=l&&r<=t) return sum[rt]; return Sum(ls[rt],l,mid,s,t)+Sum(rs[rt],mid+1,r,s,t); } int Get(int rt,int l,int r,int rank) { if(l==r)//保证合法 不用判rt { return l; } if(rank<=sum[ls[rt]]) return Get(ls[rt],l,mid,rank); else return Get(rs[rt],mid+1,r,rank-sum[ls[rt]]); } int main() { freopen("phs.in","r",stdin); freopen("phs.out","w",stdout); int T; scanf("%d",&T); while(T--) { int opt,x; scanf("%d%d",&opt,&x); if(opt==1) { Add(RT,-INF,INF,x,1); } if(opt==2) { Add(RT,-INF,INF,x,-1); } if(opt==3) { printf("%d ",Sum(RT,-INF,INF,-INF,x-1)+1); } if(opt==4) { printf("%d ",Get(RT,-INF,INF,x)); } if(opt==5) { int tmp=Sum(RT,-INF,INF,-INF,x-1)+1;//rank x printf("%d ",Get(RT,-INF,INF,tmp-1)); } if(opt==6) { int tmp=Sum(RT,-INF,INF,-INF,x)+1;//rank x+1 printf("%d ",Get(RT,-INF,INF,tmp)); } } return 0; }
那么这一大堆东西到底是什么意思呢?
权值线段树 就是线段树里不是下标 是值域
然后我们这里有几个操作
操作一 就是插入操作嘛
就把那个数值的个数+1
操作二 删除操作
一样的 就是数值的个数-1
操作三 查询x数的排名
就是查找值域范围从-INF到x-1内一共有多少个数 再加上一个1就行了
操作四 查询排名为x的数是什么
可以写一个Get函数来进行一下二分
这一段到底什么意思
就是说如果在左儿子里 就往左走
要是在右儿子里 不仅要往右走 还有要 rank-左边一共有多少个数 因为左边的那些数都比当给钱要查找的这个排名是rank的数要小
操作五 求小于x的最大的数
这个操作吗 就是查找x个排名-1的数就行了
操作六 求大于x的最小的数
我们可以加一个数
求一下x+1的排名 在查找一下那个排名的位置的数值是什么
听起来非常玄学 就是我们可以加一个x+1这样一个虚拟的数(存不存在不要紧) 只是先查出来它的排名而已
最后一点非常重要 要动态开点 不要用p*2 和p*2+1的那种存储方式 会炸内存的
一共只需要开log(len)*n的数组大小就行了
len就是值域总范围 在这一道题中是2e7