首先看到n的个数很小,但是数据范围很大的情况下,我们一般优先会选择离散化然后再建树 。
故第一步我们要先对其进行一个离散化 。
第二步是关键思想 - 可持久化数据结构,这是一个保存历史数据然后解决问题的方法, 在这里,我们保留每添加一个数的数据作为历史线段树 , 然后对其进行一个类似于前缀和的路子进行查找答案。
思路很简单,但是具体的细节仍需代码实现去完善逻辑思维。
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cstdio>
using namespace std ;
const int N = 2e5 + 10 ;
int read()
{
int res = 0 , flag = 1 ;
char c = getchar() ;
while(!isdigit(c))
{
if(c == '-') flag = -1 ;
c = getchar() ;
}
while(isdigit(c))
{
res = (res << 1) + (res << 3) + (c ^ 48) ;
c = getchar() ;
}
return res * flag ;
}
struct Node
{
int l , r ;
int val ;
} tr[N * 40] ;
int cnt , root[N] ;
vector<int> res ;
int n , m , Datas[N] ;
int get_id(int x)
{
return lower_bound(res.begin() , res.end() , x) - res.begin();
}
void build(int &x , int l , int r)
{
x = ++ cnt ;
tr[x].val = 0 ;
if(l == r) return ;
int mid = l + r >> 1 ;
build(tr[x].l , l , mid) ;
build(tr[x].r , mid + 1 , r) ;
}
void insert(int l , int r , int pre , int &now , int pos)
{
now = ++ cnt ;
tr[now] = tr[pre] ;
tr[now].val ++ ;
if(l == r) return ;
int mid = l + r >> 1 ;
if(pos <= mid) insert(l , mid , tr[pre].l , tr[now].l , pos) ;
else insert(mid + 1 , r , tr[pre].r , tr[now].r , pos) ;
}
int query(int l , int r , int x , int y , int k)
{
if(l == r) return l ;
int tmp = tr[tr[y].l].val - tr[tr[x].l].val ;
int mid = l + r >> 1 ;
if(tmp >= k) return query(l , mid , tr[x].l , tr[y].l , k) ;
else return query(mid + 1 , r , tr[x].r , tr[y].r , k - tmp) ;
}
int main(void)
{
n = read() , m = read() ;
build(root[0] , 1 , n) ;
res.push_back(-1e9 + 2) ;
for(int i = 1 ; i <= n ; i ++)
{
int t = read() ;
res.push_back(t) ;
Datas[i] = t ;
}
sort(res.begin() , res.end()) ;
res.erase(unique(res.begin() , res.end()) , res.end()) ;
for(int i = 1 ; i <= n ; i ++)
insert(1 , n , root[i - 1] , root[i] , get_id(Datas[i])) ;
for(int i = 1 ; i <= m ; i ++)
{
int l , r , k ;
l = read() , r = read() , k = read() ;
printf("%lld\n" , res[query(1 , n , root[l - 1] , root[r] , k)]) ;
}
}