RMQ问题就是range minimum query,范围最小值问题:给出n个元素的数组,要求查询操作query(l,r)来查询区间[l,r]内的最小值,同样,也可以拓展延伸到求最大值,求最大出现频率。
这类问题使用sparse_table算法可以做到O(nlogn)预处理时间复杂度,O(1)的查询时间复杂度。在这个二维数组构建的表中,d(i,j)表示从i开始,长度为2^j的范围内的最小值,因为2^32就已经是很大的数了,所以j一般开比较小就够用了,题目数据量范围大也不容易出现数组开不了这么大的情况。
void rmq_init(){
for (int i = 0; i <n; i++){ d[i][0] = a[i]; }
for (int j = 1; (1 << j) <= n; j++){
for (int i = 0; i + (1 << j) - 1 <n; i++){
d[i][j] = min(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]);
}
}
}
int rmq(int l, int r){
if (l > r)return 0;
int k = 0;
while ((1 << (k + 1)) <= r - l + 1)k++;
return min(d[l][k], d[r - (1 << k)+1][k]);
}
这里给出了预处理和查询的代码,预处理就通过循环从j为0往上递推,每次d[i][j] = max(d[i][j - 1], d[i + (1 << (j - 1))][j - 1])这里max里面的两个区间是刚好相邻不重叠的,但是查询时候的区间可能会重叠,所以可能只能在求最大值最小值这类问题使用。
UVA - 11235 Frequent values
这题题意就是在非降序数列里查询区间内最频繁的出现次数,用到了游程编码将相同的数字化为一个点来建立稀疏表。因此在数据输入时处理一下然后问题就转化成类似RMQ的问题了。
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;
int n,q;
int d[100005][32];
int lef[100005], rig[100005], num[100005], val[100005], cou[100005];
int qqq;
void rmq_init(){
for (int i = 0; i <=qqq; i++){ d[i][0] = cou[i]; }
for (int j = 1; (1 << j) <= qqq+1; j++){
for (int i = 0; i + (1 << j) - 1 <=qqq; i++){
d[i][j] = max(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]);
}
}
}
int rmq(int l, int r){
if (l > r)return 0;
int k = 0;
while ((1 << (k + 1)) <= r - l + 1)k++;
return max(d[l][k], d[r - (1 << k)+1][k]);
}
int main(){
int x, y;
while (scanf("%d", &n), n){
qqq = -1;
scanf("%d", &q);
for (int i = 1; i <= n; i++){
scanf("%d", &x);
if (qqq == -1 || x != val[qqq]){
val[++qqq] = x; lef[qqq] = i; rig[qqq] = i; cou[qqq] = 1; num[i] = qqq;
}
else{
cou[qqq]++; rig[qqq] = i; num[i] = qqq;
}
}
rmq_init();
int ans;
for (int i = 0; i < q; i++){
scanf("%d%d", &x, &y);
if (num[x] == num[y]){
ans = y - x + 1;
}
else{
int l = num[x], r = num[y];
ans = max(rig[l]-x+1,max(rmq(l+1,r-1),y-lef[r]+1));
}
printf("%d\n", ans);
}
}
return 0;
}