题意:给一个长度n的数列,和m次查询,每次输出区间[l,r]中第k小的数。
思路:划分树裸题,跟poj2104差不多。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int maxn=100005;
int tree[20][maxn], toLeft[20][maxn], sorted[maxn];
void build(int level, int l, int r){
if(l == r) return;
int mid = (l + r) >> 1;
int cnt = mid - l + 1;
for(int i=l; i<=r; ++i){
if(tree[level][i] < sorted[mid]){
--cnt;
}
}
int lpos = l, rpos = mid + 1;
for(int i=l; i<=r; ++i){
toLeft[level][i] = toLeft[level][i - 1];
if(tree[level][i] < sorted[mid]){
tree[level + 1][lpos++] = tree[level][i];
++toLeft[level][i];
} else if(tree[level][i] > sorted[mid]){
tree[level + 1][rpos++] = tree[level][i];
} else {
if(cnt){
tree[level + 1][lpos++] = tree[level][i];
++toLeft[level][i];
--cnt;
} else {
tree[level + 1][rpos++] = tree[level][i];
}
}
}
build(level + 1, l, mid);
build(level + 1, mid + 1, r);
}
int query(int level, int l, int r, int ql, int qr, int k){
if(ql == qr){
return tree[level][ql];
}
int s1 = toLeft[level][ql - 1] - toLeft[level][l - 1];
int s2 = toLeft[level][qr] - toLeft[level][ql - 1];
int mid = (l + r) >> 1;
if(s2 >= k){
return query(level + 1, l, mid, l + s1, l + s1 + s2 - 1, k);
} else {
return query(level + 1, mid + 1, r, mid + ql - l - s1 + 1, mid - l + qr - s1 - s2 + 1, k - s2);
}
}
int main(){
int t, n, m, l, r, k;
scanf("%d", &t);
while(t--){
scanf("%d%d", &n, &m);
for(int i=1; i<=n; ++i){
scanf("%d", &tree[0][i]);
sorted[i] = tree[0][i];
}
sort(sorted + 1, sorted + n + 1);
build(0, 1, n);
while(m--){
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", query(0, 1, n, l, r, k));
}
}
return 0;
}