多次查询区间第K大
法一:一次排序+暴力,6907MS
#include <cstdio>
#include <algorithm>
#define N 100007
int ary[N], id[N];
bool cmp(int x, int y) {
return ary[x] < ary[y];
}
int main()
{
int n, m;
while(~scanf("%d%d", &n, &m)) {
for (int i = 1; i <= n; ++i) {
scanf("%d", ary + i);
id[i] = i;
}
std::stable_sort(id + 1, id + 1 + n, cmp);
while(m-- > 0) {
int x, y, k;
scanf("%d%d%d", &x, &y, &k);
for (int i = 1; i <= n; ++i) {
k -= (x <= id[i] && id[i] <= y);
if (k == 0) {
printf("%d\n", ary[id[i]]);
break;
}
}
}
}
return 0;
}
\
划分树解法 954MS (就是快些)静态区间查找第k大
\
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 100007
int splay[20][N], sorted[N];
int toLeft[20][N];//第i层第[1:j]之间有多少个数据放到了左边
void build(int lv, int x, int y) {
if (x == y) return;
int mid = x + y >> 1;
int suppose = mid - x + 1; //假设中位数sorted[mid]左边的数全部小于sorted[mid]
for (int i = x; i <= y; ++i) {
if (splay[lv][i] < sorted[mid])
suppose--;
}
//如果suppose == 1, 则数组中值为sorted[mid]只有一个数
int lpos = x, rpos = mid + 1;
for (int i = x; i <= y; ++i) {
if (i == x) //预处理,相当于初始化
toLeft[lv][i] = 0; //lv层[1:i]之间有多少个数据放到了左边
else
toLeft[lv][i] = toLeft[lv][i - 1];
if (splay[lv][i] < sorted[mid]) { //划分到中位数的左边
toLeft[lv][i]++;
splay[lv + 1][lpos++] = splay[lv][i];
}
else if (splay[lv][i] > sorted[mid]) { //划分到中位数右边
splay[lv + 1][rpos++] = splay[lv][i];
}
else { //这里suppose大于0的数划分到左边
if (suppose != 0) {
suppose--;
toLeft[lv][i]++;
splay[lv + 1][lpos++] = splay[lv][i];
}
else
splay[lv + 1][rpos++] = splay[lv][i];
}
}
build(lv + 1, x, mid);
build(lv + 1, mid + 1, y);
}
int query(int lv, int l, int r, int L, int R, int k) {
//在[L, R]数据中查询第k大の数据
if (l == r) return splay[lv][l]; //该句换为 if(L == R) return splay[lv][L];//也能过,时间不一样。我有点晕了
int s;//代表[l, L)之间有所少个元素被分到左边
int ss;//[L, R]内将被分到左子树的元素数目
int mid = l + r >> 1;
if (l == L) {
s = 0;
ss = toLeft[lv][R];
}
else {
s = toLeft[lv][L - 1];
ss = toLeft[lv][R] - s;
}
int newl, newr;
if (k <= ss) { //查询左边
newl = l + s;
newr = l + s + ss - 1;
return query(lv + 1, l, mid, newl, newr, k);
}
else { //查询右边
newl = mid - l + 1 + L - s;
newr = mid - l + 1 + R - s - ss;
return query(lv + 1, mid + 1, r, newl, newr, k - ss);
}
}
int main()
{
int n, m;
while (~scanf("%d%d", &n, &m)) {
memset(splay, 0, sizeof splay);
for (int i = 1; i <= n; ++i) {
scanf("%d", &splay[0][i]);
sorted[i] = splay[0][i];
}
std::sort(sorted + 1, sorted + 1 + n);
build(0, 1, n);
while (m-- > 0) {
int x, y, k;
scanf("%d%d%d", &x, &y, &k);
printf("%d\n", query(0, 1, n, x, y, k));
}
}
return 0;
}
\
\
归并树解法2532MS
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 100007;
int T[20][N], a[N];
void build(int lv, int l, int r) {
if (l == r) {
T[lv][l] = a[l];
return;
}
int mid = l + r >> 1;
build(lv + 1, l, mid);
build(lv + 1, mid + 1, r);
//递归简单吧,下面熟悉的归并排序的合并过程
int lpos = l, rpos = mid + 1, cur = l;
while (lpos <= mid && rpos <= r) {
if (T[lv + 1][lpos] <= T[lv + 1][rpos])
T[lv][cur++] = T[lv + 1][lpos++];
else T[lv][cur++] = T[lv + 1][rpos++];
}
while (lpos <= mid) T[lv][cur++] = T[lv + 1][lpos++];
while (rpos <= r) T[lv][cur++] = T[lv + 1][rpos++];
}
int query(int lv, int l, int r, int L, int R, int key) { //返回区间[L,R]中不大于key的数的个数
if (R < l || L > r) return 0;
if (L <= l && r <= R)
return lower_bound(T[lv] + l, T[lv] + r + 1, key) - &T[lv][l];
int mid = l + r >> 1;
return query(lv + 1, l, mid, L, R, key) + query(lv + 1, mid + 1, r, L, R, key);
}
int solve(int n, int L, int R, int k) {
int low = 1, high = n + 1;
while (low + 1 < high) {
int mid = low + high >> 1;
int cnt = query(0, 1, n, L, R, T[0][mid]);
if (cnt <= k) low = mid;
else high = mid;
}
return T[0][low];
}
int main()
{
int n, m;
memset(T, 0, sizeof T);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%d", a + i);
build(0, 1, n);
while (m--) {
int L, R, k;
scanf("%d%d%d", &L, &R, &k);
printf("%d\n", solve(n, L, R, k - 1));
}
return 0;
}
\
\
\
\
\