第k小
#include<cstdio>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = 1e5+5;
int cnt,root[maxn],a[maxn];
//root[i] 第i课线段树根节点的位置
//cnt 用作开辟新的树节点。
struct node{
int l,r;//左右儿子结点编号,因为不满足2*rt规律
int sum;//代表(l,r)区间上和是多少
}T[maxn*40];
vector<int> v;
int getid(int x){
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
/** update函数介绍
l,r 代表线段树递归的区间,x代表前一棵树的节点位置,y是后面的节点位置。
在递归的过程中,将需要修改的树节点复制到新开辟节点,改变自己的sum,
也就是自加1,顺便改变上一个的孩子节点
所以传参是引用传参;//线段树区间统计,sum代表在这个区间数的个数。
*/
//update(1,n,root[i],root[i-1],getid(a[i]));
void update(int l,int r,int &x,int y,int p){
T[++cnt] = T[y];//左右son和sum都先连接
T[cnt].sum++;
x = cnt;
if(l==r) return ;
int m = (l+r)>>1;
if(m>=p) update(l,m,T[x].l,T[y].l,p);
else update(m+1,r,T[x].r,T[y].r,p);
}
/** query函数介绍
因为是查找第K小,所以在查找时候只需要看左边孩子节点,
两棵线段树sum做差,便得到这个区间的值
比如 root[R]-root[L-1] ,则代表区间 [L,R] 的数的统计
所以 S=(R线段树左孩子的sum)-(L-1线段树左孩子的sum)
如果 S>=K(第K小),所以第K小肯定在左儿子节点,
否则,右节点,并且在右边区间再找剩下的 K-S,即可。
*/
//query(1,n,root[l],root[r],k);
int query(int l,int r,int x,int y,int k){
if(l == r) return l;
int m = (l+r)>>1;
int sum = T[T[y].l].sum - T[T[x].l].sum;
if(k<=sum) return query(l,m,T[x].l,T[y].l,k);
else return query(m+1,r,T[x].r,T[y].r,k-sum);
}
int main(){
int n,m,x,y,k;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());//去重
for(int i=1;i<=n;i++)
update(1,n,root[i],root[i-1],getid(a[i]));
for(int i=1;i<=m;i++) {
scanf("%d%d%d",&x,&y,&k);
printf("%d\n",v[query(1,n,root[x-1],root[y],k)-1]);//why -1
}
return 0;
}