给出一个有n个元素的数列,每次问区间[a, b]间第c小的数是哪个。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
#define Rep(i, l, r) for (int i = l;i <= r;++i)
#define Rrep(i, r, l) for (int i = r;i >= l;--i)
const int maxn = 1e5 + 123;
struct Solution {
/*主席树离线区间k大*/
int ls[maxn*20], rs[maxn*20];//每个节点的左右孩子节点的编号
int sum[maxn*20];
int root[maxn];
int tot;//节点数
void init() {
tot = 0;
}
//建立空树,定下线断树的形态
void build(int &rt, int l, int r) {
rt = ++tot;
sum[rt] = 0;
if (l == r) return ;
int m = (l + r) >> 1;
build(ls[rt], l, m);
build(rs[rt], m + 1, r);
}
/*每次修改只需要新建log(n)个节点*/
void updata(int last, int& rt, int l, int r, int p) {
rt = ++tot;
ls[rt] = ls[last];
rs[rt] = rs[last];
sum[rt] = sum[last] + 1;
if (l == r) return ;
int m = (l + r) >> 1;
if (p <= m) updata(ls[last], ls[rt], l, m, p);
else updata(rs[last], rs[rt], m + 1, r, p);
}
/*两颗树同时向下找,为了是相减求出区间的数出现次数的情况*/
int find(int ss, int tt,int l, int r, int k) {
if (l == r) return l;/*k大的值,不过是离散化后的,最后还要通过hash函数求出原来的值*/
int m = (l + r) >> 1;
int cnt = sum[ls[tt]] - sum[ls[ss]];
if (k <= cnt) return find(ls[ss], ls[tt], l, m, k);
return find(rs[ss], rs[tt], m + 1, r, k - cnt);/*important*/
}
}solve;
int num[maxn];
int Hash[maxn];
int main(int argc, const char * argv[])
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
// ios::sync_with_stdio(false);
// cout.sync_with_stdio(false);
// cin.sync_with_stdio(false);
int n, m;
while(~scanf("%d%d", &n, &m)) {
solve.init();
Rep(i, 1, n) {
scanf("%d", &num[i]);
Hash[i] = num[i];
}
/*离散化, 处理出所有可能出现的数字*/
sort(Hash + 1, Hash + 1 + n);
int cnt = unique(Hash + 1, Hash + 1 + n) - Hash - 1;
Rep(i, 1, n) {
num[i] = lower_bound(Hash + 1, Hash + 1 + cnt, num[i]) - Hash;
}
/*建立空树,确定线断树形态*/
solve.build(solve.root[0], 1, cnt);
Rep(i, 1, n) {
solve.updata(solve.root[i - 1], solve.root[i], 1, cnt, num[i]);
}
Rep(i, 1, m) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
int p = solve.find(solve.root[a - 1], solve.root[b], 1, cnt, c);
printf("%d\n", Hash[p]);
}
}
// showtime;
return 0;
}