給定一個長度為 m 的數組,需回答 n 次查詢:區間 [u, v] 中最大值與最小值的差。數據範圍較大,暴力查詢會超時,需用 O(1) 查詢、O(m log m) 預處理的稀疏表算法。
稀疏表是一種靜態區間查詢數據結構,利用倍增思想預計算長度為 2^k 的區間最值。查詢時將任意長度區間分解為兩個重疊的 2^k 長度區間,取其最值即可。
對於區間 [l, r],長度 len = r - l + 1,k = b[len]。
最小值 = min(fl[l][k], fl[r - 2^k + 1][k])
最大值 = max(fr[l][k], fr[r - 2^k + 1][k])
#include <iostream>
#include <cstdio>
#include <algorithm>
#define maxn 200010
using namespace std;
int a[maxn], m, n, b[maxn], fl[maxn][50], fr[maxn][50];
void solve() {
b[1] = 0;
for (int i = 2; i <= m; i++) {
b[i] = b[i - 1];
if ((1 << (b[i] + 1)) == i) b[i]++;
}
for (int i = 0; i < m; i++) fl[i][0] = fr[i][0] = a[i];
for (int i = m - 1; i >= 0; i--)
for (int j = 1; i + (1 << j) - 1 < m; j++) {
fl[i][j] = min(fl[i][j - 1], fl[i + (1 << (j - 1))][j - 1]);
fr[i][j] = max(fr[i][j - 1], fr[i + (1 << (j - 1))][j - 1]);
}
}
int qma(int l, int r) {
int len = r - l + 1;
int k = b[len];
return max(fr[l][k], fr[r - (1 << k) + 1][k]);
}
int qmi(int l, int r) {
int len = r - l + 1;
int k = b[len];
return min(fl[l][k], fl[r - (1 << k) + 1][k]);
}
int main() {
scanf("%d%d", &m, &n);
for (int i = 0; i < m; i++) cin >> a[i];
solve();
while (n--) {
int u, v;
cin >> u >> v;
u--; v--;
printf("%d\n", qma(u, v) - qmi(u, v));
}
return 0;
}
稀疏表適用於靜態數組的冪等操作最值查詢,是 RMQ 經典解法之一。