曾经,这道题是学线段树的时候拿来练手的。参见:博客 。当初写了两个线段树。这次写两个ST吧。O(logN)的查询已经不能满足需求了。像这种求区间最值的问题,就是RMQ问题,这个时候采用ST算法想必是极好的。利用了二分的思想之后,预处理是O(NlogN),而烦人的查询仅仅是O(1)。
这相当于ST算法的模板题。思路不必多说,就是将查询抽象成状态。d[i][j]。j表示2^j个数字。i表示起点。查询的时候,用左起2^j个数和右起2^j个数来覆盖查询区间。要熟悉ST的初始化。
代码如下:
/****************************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <string>
#include <iostream>
using namespace std;
/****************************************/
const int N = 100005;
int d_max[N][25], d_min[N][25];
int l[N], r[N], a[N];
void max_init(const int *A, int n)
{
for(int i = 0; i < n; i++)
d_max[i][0] = A[i];//递推的起始条件
for(int j = 1; (1<<j) <= n; j++)
for(int i = 0; i + (1<<j) - 1 < n; i++)
d_max[i][j] = max(d_max[i][j-1], d_max[i+(1<<(j-1))][j-1]);//[i+(1<<(j-1))]想一想为什么要j-1:因为是从前一个状态向后递推
}
void min_init(const int *A, int n)
{
for(int i = 0; i < n; i++)
d_min[i][0] = A[i];
for(int j = 1; (1<<j) <= n; j++)
for(int i = 0; i + (1<<j) - 1 < n; i++)
d_min[i][j] = min(d_min[i][j-1], d_min[i+(1<<(j-1))][j-1]);
}
int max_quer(int LL, int RR)
{
int k = 0;
k = log(1.0 * (RR-LL+1)) / log(2.0);//还记得为什么要k+1吗?
//如果2^(k+1)大于查询区间数的个数,那么从LL向右2^k个数与从RR向左2^k个数一定能有交集地覆盖查询区间
return max(d_max[LL][k], d_max[RR-(1<<k)+1][k]);
}
int min_quer(int LL, int RR)
{
int k = 0;
k = log(1.0 * (RR-LL+1)) / log(2.0);
return min(d_min[LL][k], d_min[RR-(1<<k)+1][k]);
}
int main()
{
int n, op;
scanf("%d%d", &n, &op);
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
max_init(a, n);
min_init(a, n);
while(op--) {
int L, R, ans;
scanf("%d%d", &L, &R);
L--; R--;
if(L == R)
ans = 0;
else
ans = max_quer(L, R) - min_quer(L, R);
printf("%d\n", ans);
}
return 0;
}