給定長度 n (≤100,000) 的數列,回答 m (≤100,000) 個查詢:區間 [s, t] 中第 k 大的數是多少。多測試用例。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100000 + 10;
struct Node { int lson, rson; int mid() { return (lson + rson) >> 1; } } tree[MAXN * 4];
int seg[25][MAXN];
int lftnum[25][MAXN];
int num[MAXN];
int n, m, sta, ed;
void build(int L, int R, int rt, int dep) {
tree[rt].lson = L; tree[rt].rson = R;
if (L == R) return;
int mid = tree[rt].mid();
int key = num[mid];
int scnt = mid - L + 1; // 左邊應分配的個數(含等於 key 的一部分)
for (int i = L; i <= R; ++i)
if (seg[dep][i] < key) --scnt;
int lp = L, rp = mid + 1;
for (int i = L; i <= R; ++i) {
lftnum[dep][i] = (i == L) ? 0 : lftnum[dep][i-1];
if (seg[dep][i] < key) {
++lftnum[dep][i];
seg[dep+1][lp++] = seg[dep][i];
} else if (seg[dep][i] > key) {
seg[dep+1][rp++] = seg[dep][i];
} else {
if (scnt > 0) { --scnt; ++lftnum[dep][i]; seg[dep+1][lp++] = seg[dep][i]; }
else { seg[dep+1][rp++] = seg[dep][i]; }
}
}
build(L, mid, rt<<1, dep+1);
build(mid+1, R, rt<<1|1, dep+1);
}
int query(int L, int R, int rt, int dep, int k) {
if (tree[rt].lson == tree[rt].rson) return seg[dep][L];
int cnt, act;
if (L == tree[rt].lson) { cnt = lftnum[dep][R]; act = 0; }
else { cnt = lftnum[dep][R] - lftnum[dep][L-1]; act = lftnum[dep][L-1]; }
int mid = tree[rt].mid();
if (cnt >= k) {
L = tree[rt].lson + act;
R = tree[rt].lson + act + cnt - 1;
return query(L, R, rt<<1, dep+1, k);
} else {
int a = L - tree[rt].lson - act;
int b = R - L - cnt + 1;
L = mid + a + 1;
R = mid + a + b;
return query(L, R, rt<<1|1, dep+1, k - cnt);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T; cin >> T;
while (T--) {
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> num[i];
seg[0][i] = num[i];
}
sort(num+1, num+n+1);
build(1, n, 1, 0);
while (m--) {
int k; cin >> sta >> ed >> k;
cout << query(sta, ed, 1, 0, k) << '\n';
}
}
return 0;
}
劃分樹是解決靜態區間第 K 大查詢的經典數據結構,利用快排 partition 思想將值域劃分,配合前綴和快速定位子區間,實現對數級查詢。掌握其構建與查詢映射邏輯是攻克此類問題的關鍵。