原题:https://www.luogu.org/problemnew/show/P3834
题解:求区间中第k小的元素。这里要用到主席树。主席树的思想是对数列的每一个前缀都建一个线段树。由于数据很大首先考虑离散化。在处理[l,r]的区间时,只要找到第l-1棵树和第r棵树,对应节点的差就是该询问对应区间信息。但由于这样做内存很大,我们考虑动态开点,只需要建一颗空树,开用到的点就行了,空间复杂度为O(N*log(N))。查询时只用判断第k小在左子树还是右子树就行了。
#include<bits/stdc++.h>
#define mid (l+r)/2
using namespace std;
const int N=220000;
const int Log=20;
int sum[N*Log],T[N],L[N*Log],R[N*Log];
int n,q,m,a[N],b[N],tot;
inline int rd(){
int x=0;int f=1;char s=getchar();
while(!isdigit(s)) f=(s=='-'?-1:f),s=getchar();
while(isdigit(s)) x=(x<<1)+(x<<3)+s-'0',s=getchar();
return x*f;
}
inline int build(int l,int r){
int rt=++tot;sum[rt]=0;
if(l<r){
L[rt]=build(l,mid);
R[rt]=build(mid+1,r);
}
return rt;
}
inline int update(int pre,int l,int r,int x){
int rt=++tot;
L[rt]=L[pre];R[rt]=R[pre];sum[rt]=sum[pre]+1;
if(l<r){
if(x<=mid) L[rt]=update(L[pre],l,mid,x);
else R[rt]=update(R[pre],mid+1,r,x);
}
return rt;
}
inline int query(int u,int v,int l,int r,int k){
if(l>=r) return l;
int x=(sum[L[v]]-sum[L[u]]);
if(x>=k) return query(L[u],L[v],l,mid,k);
else return query(R[u],R[v],mid+1,r,k-x);
}
int main(){
// freopen("p3834.in","r",stdin);
n=rd();q=rd();
for(int i=1;i<=n;i++) b[i]=a[i]=rd();
sort(a+1,a+n+1);
m=unique(a+1,a+n+1)-(a+1);
tot=0;T[0]=build(1,m);//建空树
for(int i=1;i<=n;i++) {
int t=lower_bound(a+1,a+m+1,b[i])-a;
T[i]=update(T[i-1],1,m,t);
}
for(int i=1;i<=q;i++){
int x=rd();int y=rd();int k=rd();
int p=query(T[x-1],T[y],1,m,k);
printf("%d\n",a[p]);
}
return 0;
}