📅  最后修改于: 2023-12-03 15:06:51.240000             🧑  作者: Mango
本文将介绍使用 sqrt 分解进行 RMQ(最小值查询),RMQ 是一种经典问题,在许多数据结构和算法中都有涉及,它的目标是在一段区间内查询最小值。我们通常使用一些数据结构,例如线段树,ST 表等,但在这里,我们将介绍另一种称为 sqrt 分解的方法。
给定一个包含 n 个元素的数组 a,以及 m 个询问,每个询问包含两个整数 l 和 r(1 <= l <= r <= n),询问数组 a 在区间 [l, r] 范围内的最小值。
sqrt分解算法的思路如下:
由于每个块中的元素数量为 $sqrt(n)$,那么处理每个块的最小值可以使用 O($sqrt(n)$) 的时间复杂度,在 $m$ 个询问中,每一次询问最差情况下需要处理的区间数量为 $3$,以及每个完整块的数量为 $\frac{n}{sqrt(n)}=\sqrt(n)$,因此总时间复杂度为 O($m\sqrt(n)$)。
首先,我们需要先预处理出所有块中的最小值和它所代表的位置。
const int MAXN = 1e5;
const int SQRN = 316; // sqrt(MAXN)
int n, a[MAXN + 5], b[SQRN + 5], pos[MAXN + 5], S;
void prework() {
S = sqrt(n);
for (int i = 1; i <= n; i++) {
b[pos[i] = (i - 1) / S + 1] = i;
if (i % S == 1) {
int f = pos[i];
for (int j = i; j <= min(n, i + S - 1); j++) {
if (a[j] < a[b[f]]) b[f] = j;
}
}
}
}
然后,我们需要分别计算出 l、r 所在块中的最小值,并计算所有完整块的最小值,比较它们之中的最小值。
int query(int l, int r) {
int ans = INT_MAX;
if (pos[l] == pos[r]) {
for (int i = l; i <= r; i++) ans = min(ans, a[i]);
return ans;
}
for (int i = l; pos[i] == pos[l]; i++) ans = min(ans, a[i]);
for (int i = r; pos[i] == pos[r]; i--) ans = min(ans, a[i]);
for (int i = pos[l] + 1; i < pos[r]; i++) ans = min(ans, a[b[i]]);
return ans;
}
最后,我们将预处理和查询整合到一个主函数中:
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
prework();
int m;
cin >> m;
while (m--) {
int l, r;
cin >> l >> r;
cout << query(l, r) << endl;
}
return 0;
}
这篇文章介绍了如何使用 sqrt 分解方法解决 RMQ 问题,它的时间复杂度为 O($m\sqrt(n)$),要比线段树和 ST 表更加高效。同时,这种方法也可以用来解决其他问题,例如区间众数查询。