题目就一句话,找到一个区间里第k小的数,详细看代码
//主席树模板题
//求区间第 k 小的树
#include<stdio.h>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=1e5+6;
int n,m,cnt,root[maxn],a[maxn],x,y,k;
//cnt是root的计数,a存放原来的数据。
struct node{
int l,r,sum;
}hjt[maxn*20];
vector<int>v;//用一个容器来离散化操作
int getid(int x){
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;//+1
}//我们只关心数的相对大小,不关心具体是多少,所以离散化。
//离散化时使用,v是排序去重后的,得到第一个大于或等于x的位置,就是这个数在所有数里的相对大小。
void insert(int l,int r,int pre,int &now,int p){//pre是前一个,now是现在的,插入一个树,就建立一个新版本的主席树,最初没有数据的位置相当于空
//主要思路是复制旧版,修改需要的位置。
hjt[++cnt]=hjt[pre];//复制旧版
now=cnt;//now的值是hjt的序号
hjt[now].sum++;//插入了一个数,总个数+1
if(l==r)//到了叶子节点,直接返回
{return;}
int m=(l+r)>>1;
//因为维护的是区间数的个数,所以插入数p 就是插在位置p。
if(p<=m) {insert(l,m,hjt[pre].l,hjt[now].l,p);}
else {insert(m+1,r,hjt[pre].r,hjt[now].r,p);}
}
int query(int l,int r,int L,int R,int k){
if(l==r) {return l;}
int m=(l+r)>>1;
int tmp=hjt[hjt[R].l].sum-hjt[hjt[L].l].sum;
//R版本左孩子的sum减掉L版本左孩子的sum,为所查询的左孩子中数的个数,
//如果比k小,就在右孩子里查询第k-tmp个数,如果比k大,就在左孩子里查询第k个
if(k<=tmp) {return query(l,m,hjt[L].l,hjt[R].l,k);}
else {return query(m+1,r,hjt[L].r,hjt[R].r,k-tmp);}
}
int main(){
int cas;
scanf("%d",&cas);
while (cas--)
{
cnt=0;
v.clear();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
v.push_back(a[i]);
}//把a[i]放在容器里
sort(v.begin(),v.end());//排序
v.erase(unique(v.begin(),v.end()),v.end());//去重
for(int i=1;i<=n;i++)
insert(1,n,root[i-1],root[i],getid(a[i]));//建立主席树
while(m--)
{
scanf("%d%d%d",&x,&y,&k);
printf("%d\n",v[query(1,n,root[x-1],root[y],k)-1]);
//每次插入一个就有一个版本的主席树,所以用 y版本的 减掉 x-1版本的,就是所查询区间的所有数组成的树。
}
}
return 0;
}