K-th Number
问题描述
给定一个数组 a[1...n],数组元素各不相同,你的程序要对每次查询Q(i,j,k)作出回答,其中Q(i,j,k)的含义为在数组a[i...j]中第k大的数字.
例如,给出数组a=(1, 5, 2, 6, 3, 7, 4).查询语句为Q(2, 5, 3),即从(5,2,6,3)中找出第3大的元素,将之排序得到(2, 3, 5, 6),故第三大的数字是5,所以这次查询的结果应当为5.
输入格式
数据第1行有两个数字N与M,分别表示数组元素的个数与查询次数(1 <= n <= 100000, 1 <= m <= 5000).
第2行有N个数字,表示数组中的各元素,数组中各元素互不相同,并且每个数字的绝对值不超过109.
接下来的m行,每行包含3个数字i,j,k,(1 <= i <= j <= n, 1 <= k <= j - i + 1),表示一次查询Q(i,j,k)
输出格式
对于每次查询,输出查询结果,即a[i...j]中第k大的数字,每个结果占一行
Sample Input
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
Sample Output
5
6
3
Hint
输入数据较多,请使用C语言风格的输入输出语句(scanf(),printf()),否则可能发生Time Limit Exceed.
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
using namespace std;
const int N = 1e5 + 7;
const int M = 4e6 + 7; //太大爆
int root[N], tot; //根,总数
struct Tree {
int l, r, sum;
} tree[M];
vector<int> v; //离散化
int a[N]; //原数组
int getid(int x) { //hash
return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}
void update(int l, int r, int L, int &R, int pos) {
tree[++tot] = tree[L], tree[tot].sum++, R = tot;
if (l == r) return;
int mid = (l + r) / 2;
if (pos <= mid) update(l, mid, tree[L].l, tree[R].l, pos); //只更新一条链
else update(mid+1, r, tree[L].r, tree[R].r, pos);
}
int query(int l, int r, int L, int R, int k) {
if (l == r) return l;
int mid = (l + r) / 2;
int sum = tree[tree[R].l].sum - tree[tree[L].l].sum;
if (sum >= k) return query(l, mid, tree[L].l, tree[R].l, k);
else return query(mid+1, r, tree[L].r, tree[R].r, k-sum);
}
int main() {
int n, q;
while (~scanf ("%d %d", &n, &q)) {
for (int i = 1; i <= n; ++i) scanf ("%d", &a[i]), v.push_back(a[i]);
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end()); //去重
for (int i = 1; i <= n; ++i) {
update(1, n, root[i-1], root[i], getid(a[i]));
}
int l, r, k; //求[l, r]排序后的第k个数
while (q--) {
scanf ("%d %d %d", &l, &r, &k);
printf ("%d\n", v[query(1, n, root[l-1], root[r], k)-1]);
}
}
return 0;
}