#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define p_queue priority_queue
const int MAXN = 200005;
int n , m;
int size, len;
int a[MAXN], b[MAXN];
int lc[MAXN<<5], rc[MAXN<<5], sum[MAXN<<5], root[MAXN];
//lc某点左儿子编号,rc某点右儿子编号,sum某点所维护的值,root用于存储若干线段树根节点的编号
void build(int& rt, int l , int r) //采用引用,同时给左右儿子编号,&rt相当于lc[rt],rt变换,lc[rt]也会变换
{
rt = ++ size; //用于节点编号,和线段树有所不同,不能根据公示得到左右儿子,需要动态编号
sum[rt] = 0;
if(l == r)
return ;
int mid = (l+r) >> 1;
build(lc[rt], l , mid); //左儿子
build(rc[rt],mid+1,r); //右儿子
//如果这种不理解,下面的build也是可以的
}
/*
int build(int l, int r)
{
++ size;
sum[size] = 0;
if(l == r)
return 0;
int mid = (l+r) >> 1;
lc[size] = build(l , mid);
rc[size] = build(mid+1,r);
return size;
}
*/
int update(int pre , int l , int r, int temp)
{
int now = size ++; //新建一个节点
//先将新建节点的左右儿子,指向上一颗线段树的同位置的左右儿子
lc[now] = lc[pre], rc[now] = rc[pre], sum[now] = sum[pre] + 1;
if(l == r) return now;
int mid = (l + r) >> 1;
//如果新建节点的左儿子需要更新则更新左儿子,让当前新建节点的指向改变,每次要么只更新左或者右,另外一个儿子指向上一颗树的同位置
if(mid >= temp)
lc[now] = update(lc[now],l,mid,temp);
else
rc[now] = update(rc[now],mid+1,r,temp);
return now;
}
int query(int u , int v, int l , int r, int k)
{
//两颗线段树做差之后,就相当于是[l-1,r]区间构成的线段树了,就可以转换为权值线段树Kth操作了
int mid = (l + r) >> 1, x = sum[lc[v]] - sum[lc[u]];
if(l == r) return l;
if(x >= k)
return query(lc[u],lc[v],l,mid,k);
else
return query(rc[u],rc[v],mid+1,r,k-x);
}
int main(void)
{
scanf("%d %d",&n , &m);
for(int i = 1 ; i <= n ; i ++)
scanf("%d",&a[i]), b[i] = a[i];
sort(b+1,b+1+n);
len = unique(b+1,b+1+n)-b-1;
build(root[0],1,len); //root[0] = 1,空树根节点编号是1
for(int i = 1 ; i <= n ; i ++)
{
int temp = lower_bound(b+1,b+1+len,a[i])-b;
root[i] = update(root[i-1],1,len,temp);
}
for(int i = 1 ; i <= m ; i ++)
{
int l , r, k;
scanf("%d %d %d",&l, &r, &k);
printf("%d\n",b[query(root[l-1],root[r],1,len,k)]);
}
}