#1903 第k大的数
主席树模版题
题面
你为Macrohard公司的数据结构部门工作,你的工作是重新写一个数据结构,这个数据结构能快速地找到一段数列中第k大的数。
就是说,给定一个整数数列a[1…n],其中每个元素都不相同,你的程序要能回答一组格式为Q (i , j , k)的查询,Q(i, j ,k)的意思是“在a[i…j]中第k大的数是多少?”
例如令 a = {1, 5, 2, 6, 3, 7, 4},查询格式为Q (2 , 5 , 3),数列段a[2…5] = {5, 2, 6, 3},第3大的数是5,所以答案是5。
输入
文件第一行包括一个正整数n,代表数列的总长度,还有一个数m,代表有m个查询。 n,m满足:1≤n≤100 000, 1≤m≤5 000 第二行有n个数,代表数列的元素,所有数都不相同,而且不会超过109 接下来有m行,每行三个整数i , j , k,代表一次查询, i , j , k满足1≤i≤j≤n, 1≤k≤j − i + 1
输出
输出每个查询的答案,用换行符隔开
样例输入
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
样例输出
5
6
3
SOL
对于求第k大问题,我们可能会想用线段树来维护,方法类似于前缀和,但是想求任意区间的第k大,线段树要造N棵,容易MLE。ssldw说也可以用分块来做,但是写得丑就会TLE……
容易看出这道题满足区间减法——求区间[L,R]的第k大,就是要找出[L,R]中有k个数大于x,若[1,L-1]中有s1个数大于x,[1,R]中有s2个数大于x,那么[L,R]中就会有s2-s1个数大于x。
于是就想到了主席树(?)。
主席树的特点就在于她只用增加改变过的节点,历史版本中未改变的节点会被直接复制的当前版本的树上,不会增加节点。满足区间减法的题常常可以考虑主席树。
代码:
#include<bits/stdc++.h>
#define N 100100
#define M 100100
int n,m;
int a[N],h[N];
using namespace std;
inline int rd(){
int w=1,data=0;static char ch='0';
ch=getchar();
while((ch!='-')&&(!isdigit(ch)))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(isdigit(ch))data=(data<<1)+(data<<3)+ch-'0',ch=getchar();
return data*w;
}
inline void write(int x){
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
int root,tot;
struct tree{
int sum,l,r,id;
}t[N<<5];
inline int build(int l,int r){
++tot;int root=tot;t[root].sum=0;
if(l<r){
int mid=l+r>>1;
t[root].l=build(l,mid);
t[root].r=build(mid+1,r);
}
return root;
}
inline int update(int p,int ql,int qr,int pre){
++tot;int root=tot;
t[root].l=t[pre].l,t[root].r=t[pre].r,t[root].sum=t[pre].sum+1;//如果没有更改,就将前一棵树的信息全部复制过来
if(ql<qr){
int mid=ql+qr>>1;
if(p<=mid)t[root].l=update(p,ql,mid,t[pre].l);//有更改则对当前版本的根节点做改动
else t[root].r=update(p,mid+1,qr,t[pre].r);
}
return root;
}
inline int query(int ql,int qr,int u,int v,int k){
if(ql>=qr)return ql;
int mid=ql+qr>>1;
int num=t[t[v].l].sum-t[t[u].l].sum;//在左边搜左边,在右边搜右边
if(num>=k)return query(ql,mid,t[u].l,t[v].l,k);
else return query(mid+1,qr,t[u].r,t[v].r,k-num);
}
int main(){
int n=rd(),m=rd();
for(int register i=1;i<=n;i++){
a[i]=rd();
h[i]=a[i];//hash
}
sort(h+1,h+n+1);
int nn=unique(h+1,h+n+1)-h-1;
t[0].id=build(1,nn);
for(int register i=1;i<=n;i++){
int pos=lower_bound(h+1,h+n+1,a[i])-h;
t[i].id=update(pos,1,nn,t[i-1].id);
}
while(m--){
int register u=rd(),v=rd(),k=rd();
int pos=(query(1,nn,t[u-1].id,t[v].id,k));
write(h[pos]);putchar('\n');
}return 0;
}