//动态开点可持久化权值线段树
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
struct Segmentree
{
int ls,rs,sum;
}t[N<<5];
int rt[N],tot=0,n,m,a[N],b[N],p;//p含义为修改点
void build(int p,int l,int r)
{
p=++tot;
if(l==r) return;
int mid=(l+r)>>1;
build(t[p].ls,l,mid);
build(t[p].rs,mid+1,r);
}
int update(int root,int l,int r)
{
int op=++tot;
t[op].ls=t[root].ls,t[op].rs=t[root].rs,t[op].sum=t[root].sum+1;
if(l==r) return op;//左端点等于右端点,可以结束了
int mid=(l+r)>>1;
if(p<=mid) t[op].ls=update(t[op].ls,l,mid);
else t[op].rs=update(t[op].rs,mid+1,r);
return op;
}
long long query(int u,int v,int l,int r,int k)
{
long long ans=0;int mid=(l+r)>>1;
int x=t[t[v].ls].sum-t[t[u].ls].sum;
if(l==r) return l;
if(x>=k) ans=query(t[u].ls,t[v].ls,l,mid,k);
else ans=query(t[u].rs,t[v].rs,mid+1,r,k-x);//此处递归到右子树需要减掉左子树已经拥有的排名
return ans;
}
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
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;//离散化,sort unique
build(rt[0],1,len);//建空树
for(int i=1;i<=n;i++)
{
p=lower_bound(b+1,b+1+len,a[i])-b;
rt[i]=update(rt[i-1],1,len);//一个一个加上去
}
for(int i=1;i<=m;i++)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
long long ans=query(rt[l-1],rt[r],1,len,k);
//cout<<ans<<endl;
printf("%d\n",b[ans]);
}
return 0;
}