主席树原理就是区间内的第k大可以通过每个数字的出现次数随便乱加减搞出来,然后,就可以像前缀和那样建n个线段树,就可以查询区间第k大了!
主要问题就是空间绝对爆炸,大概
看这个图:(来自blog)
可以发现每一颗线段树结构都相同,只是每次都有
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=200001;
int n,m,size,b[maxn],v[maxn],tot,root[maxn];
struct Node{
int l,r,size;
}a[maxn*30];
void build(int l,int r,int &node){
node=++tot;
if(l==r)return;
int mid=l+r>>1;
build(l,mid,a[node].l);
build(mid+1,r,a[node].r);
}
void addtree(int l,int r,int &node,int pre,int x){
node=++tot;
a[node]=a[pre];
a[node].size++;
if(l==r)return;
int mid=l+r>>1;
if(x<=mid)addtree(l,mid,a[node].l,a[pre].l,x);
else addtree(mid+1,r,a[node].r,a[pre].r,x);
}
int query(int l,int r,int lx,int rx,int k){
if(l==r)return l;
int lsize=a[a[rx].l].size-a[a[lx].l].size;
int mid=l+r>>1;
if(k<=lsize)return query(l,mid,a[lx].l,a[rx].l,k);
else return query(mid+1,r,a[lx].r,a[rx].r,k-lsize);
}
inline int hash1(int k){
return lower_bound(b+1,b+size+1,k)-b;
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&v[i]);
b[i]=v[i];
}
sort(b+1,b+n+1);
size=unique(b+1,b+n+1)-b-1;
build(1,size,root[0]);
for(int i=1;i<=n;i++){
addtree(1,size,root[i],root[i-1],hash1(v[i]));
}
for(int i=1;i<=m;i++){
int l,r,k;
scanf("%d %d %d",&l,&r,&k);
int x=query(1,size,root[l-1],root[r],k);
printf("%d
",b[x]);
}
return 0;
}