传送门:点击打开链接
题意:区间查询第k大
思路:主席树裸题,原来以前看不懂主席树是因为不懂可持久线段树,要学习主席树,应该先学习可持久线段树,然后再学的,这样主席树就能秒懂了。
主席树就是利用可持久线段树维护前缀和,然后利用前缀和就能查询中间的第k大了。
#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;
const int MX = 5e5 + 5;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int A[MX], B[MX], rear;
int S[MX << 2], ls[MX << 2], rs[MX << 2], o[MX], sz;
void push_up(int rt) {
S[rt] = S[ls[rt]] + S[rs[rt]];
}
void build(int l, int r, int &rt) {
rt = ++sz;
if(l == r) {
S[rt] = 0;
return;
}
int m = (l + r) >> 1;
build(l, m, ls[rt]); build(m + 1, r, rs[rt]);
push_up(rt);
}
void update(int pos, int l, int r, int pre, int &rt) {
rt = ++sz;
if(l == r) {
S[rt] = S[pre] + 1;
return;
}
int m = (l + r) >> 1;
ls[rt] = ls[pre]; rs[rt] = rs[pre];
if(pos <= m) update(pos, l, m, ls[pre], ls[rt]);
else update(pos, m + 1, r, rs[pre], rs[rt]);
push_up(rt);
}
int query(int k, int l, int r, int pre, int rt) {
if(l == r) return l;
int m = (l + r) >> 1, num = S[ls[rt]] - S[ls[pre]];
if(k <= num) return query(k, l, m, ls[pre], ls[rt]);
else return query(k - num, m + 1, r, rs[pre], rs[rt]);
}
int main() {
int T, n, m; //FIN;
scanf("%d", &T);
while(T--) {
sz = 0;
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);
rear = unique(B + 1, B + 1 + n) - B - 1;
build(1, n, o[0]);
for(int i = 1; i <= n; i++) {
int id = lower_bound(B + 1, B + 1 + rear, A[i]) - B;
update(id, 1, rear, o[i - 1], o[i]);
}
for(int i = 1; i <= m; i++) {
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
int id = query(k, 1, rear, o[l - 1], o[r]);
printf("%d\n", B[id]);
}
}
return 0;
}