#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=200010;
int n,m,a[N],b[N],cnt;
int rt[N<<5],L[N<<5],R[N<<5],sz[N<<5];
//rt为根节点编号
//L[i]为节点i的左子树编号,R[i]是节点i的右子树编号
//sz[i]是以节点i为根的子树的大小
inline int build(int l,int r) //建立第一颗线段树,不解释
{
int root=++cnt;
if(l==r) return root;
int mid=l+r>>1;
L[root]=build(l,mid);
R[root]=build(mid+1,r);
}
inline int add(int l,int r,int k,int pre)//添加新节点
{
int root=++cnt; sz[root]=sz[pre]+1;
//因为每次添加的线段树都比前一个线段树多一个节点
//所以sz也要比上一个线段树多1
if(l==r) return root; //如果到边界就返回
int mid=l+r>>1;
L[root]=L[pre]; R[root]=R[pre]; //借用前面线段树的节点
if(k<=mid) L[root]=add(l,mid,k,L[pre]);
//如果更新的值的位置在左子树就添加新节点到左边,并覆盖L[root]
else R[root]=add(mid+1,r,k,R[pre]);
//反之就添加新节点到右边,同样要覆盖
return root;//返回新节点的编号
}
inline int query(int l,int r,int hea,int las,int k)//查询区间第k名
{
if(l==r) return l; //如果到边界就返回节点编号
//找排名的基本操作
int mid=l+r>>1,x=sz[L[las]]-sz[L[hea]];
if(x>=k) return query(l,mid,L[hea],L[las],k);
return query(mid+1,r,R[hea],R[las],k-x);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+n+1);//排序
int len=unique(b+1,b+n+1)-b-1;//去重
rt[0]=build(1,len);//建树
for(int i=1;i<=n;i++)
{
int t=lower_bound(b+1,b+len+1,a[i])-b;//在b中找<=a[i]的最大位置
rt[i]=add(1,len,t,rt[i-1]);//加点
}
int x,y,z;
while(m--)
{
scanf("%d%d%d",&x,&y,&z);
printf("%d
",b[query(1,len,rt[x-1],rt[y],z)]);
}
return 0;
}