前言:还是要学一下线段树,以后可能会用到
可持续化线段树就是保留这一次操作没有修改的节点,动态开点
我们来看一个模板题
#include<bits/stdc++.h>
using namespace std;
const int N = (int)2e5+5;
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
int n,m,a[N];
vector<int> v;
struct node{
int ch[2];
int s; // 值域中有多少个数
}tr[N*22]; // 我们要开 n log n个节点
int root[N],idx;
// 建立一个空的值域为n的树
void build(int &x,int l,int r){
x = ++idx;
if(l==r) return;
int m = l+r >> 1;
build(lc(x),l,m);
build(rc(x),m+1,r);
}
// 每次开辟的时候左右节点只有一个是新的
void insert(int x,int &y,int l,int r,int v){
y = ++idx;
tr[y] = tr[x]; // 全盘拷贝
tr[y].s++; // 值域的个数加一
if(l==r) return;
int m = l + r >> 1;
if(m>=v) insert(lc(x),lc(y),l,m,v);
else insert(rc(x),rc(y),m+1,r,v);
}
int query(int x,int y,int l,int r,int k){
if(l==r) return l; // 返回答案
int m = l + r >> 1;
int s = tr[lc(y)].s - tr[lc(x)].s;
if(k<=s) return query(lc(x),lc(y),l,m,k);
else return query(rc(x),rc(y),m+1,r,k-s);
}
// 离散三部曲,排序,去重,二分找下标
int getid(int x){
return lower_bound(v.begin(),v.end(),x) - v.begin()+1;
}
int main(){
cin >> n >> m;
for(int i=1;i<=n;i++){
cin >> a[i];
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int len = v.size();
build(root[0],1,len); // 我们假定值域从 1 到 len
for(int i=1;i<=n;i++){
insert(root[i-1],root[i],1,len,getid(a[i]));
}
for(int i=1;i<=m;i++){
int l,r,k;
cin >> l >> r >> k;
int id = query(root[l-1],root[r],1,len,k)-1;
cout << v[id] << endl;
}
return 0;
}