#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
struct Tree{
int l,r,s;
}tree[maxn*32];//开32倍
struct node{
int w,pos;
}a[maxn];
int b[maxn],c[maxn],root[maxn];
//b 离散后的位置 c原值 root个树的根
int k=0,tot=0;
void dc(int n)//离散化
{
b[a[1].pos]=++tot;
c[tot]=a[1].w;
for(int i=2;i<=n;i++)
{
if(a[i].w!=a[i-1].w)
tot++;
b[a[i].pos]=tot;
c[tot]=a[i].w;
}
}
int cmp(node a,node b)
{
return a.w<b.w;
}
int built(int l,int r)//建树 防止爆内存 静态分配空间
{
int now=++k;
int mid=(l+r)>>1;
if(l<r)
{
tree[now].l=built(l,mid);
tree[now].r=built(mid+1,r);
}
return now;
}
int updata(int pre,int x,int l,int r)//更新
{
int mid=(l+r)>>1;
int now=++k;
tree[now].s=tree[pre].s+1;//区间内的数的出现次数++
tree[now].l=tree[pre].l;//将左右节点先指向原树
tree[now].r=tree[pre].r;
if(l<r)//新建树链
{
if(x<=mid)
tree[now].l=updata(tree[pre].l,x,l,mid);
else
tree[now].r=updata(tree[pre].r,x,mid+1,r);
}
return now;
}
int query(int u,int v,int x,int l,int r)//查询
{
int mid=(l+r)>>1;//找到第k小值
if(l==r) return l;
//第r棵线段树左儿子-第(l-1)棵线段树左儿子的值
int t=tree[tree[v].l].s-tree[tree[u].l].s;
//cout<<t<<endl;
return x<=t?query(tree[u].l,tree[v].l,x,l,mid):query(tree[u].r,tree[v].r,x-t,mid+1,r);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].w);
a[i].pos=i;
}
sort(a+1,a+1+n,cmp);
dc(n);
// for(int i=1;i<=n;i++)
// cout<<a[i].w<<" ";cout<<endl;
// for(int i=1;i<=n;i++)
// cout<<b[i]<<" ";cout<<endl;
// for(int i=1;i<=n;i++)
// cout<<c[i]<<" ";cout<<endl;
root[0]=built(1,tot);
for(int i=1;i<=n;i++)//建n棵线段树,边加点边建树
root[i]=updata(root[i-1],b[i],1,tot);
for(int i=1;i<=m;i++)
{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
// cout<<query(l-1,r,x,1,tot)<<endl;
//[l,r]就等价于 第r棵线段树-第(l-1)棵线段树 的k小值,返回该节点映射的值
printf("%d\n",c[query(root[l-1],root[r],x,1,tot)]);
}
return 0;
}