题意:
给出N个数,Q个询问。
每个询问求区间[L,R]中最大值与最小值之差。
做法:
ST算法,本质上是一种dp。
假设用二位数组来保存最大值的信息,其中max[i][j]表示从第i个数开始(每行0号元素不用,即i! = 0),长度为2^j的区间,即[i, i + 2^j-1]。
则max[i + 2 ^ j][j]表示从第(i + 2 ^ j)个数开始,长度为2 ^j的区间,即[i + 2 ^ j,(i +2 ^ j)+ 2 ^ j-1]。
很容易发现, [i, i + 2^j-1]和[i + 2 ^ j,i + 2 ^ j+2^j-1]这两个区间刚好组成了区间[i ,i + 2 ^ j+2^j-1]。即[i,i+2 ^ (j+1)-1]。
而[i,i+2 ^ (j+1)-1]表示的就是从第i个数开始,长度为2^(j+1)的区间。
于是有max[i][j+1]=MAX(max[i][j],max[i+2^j][j]
根据这种倍增的思想,可求出所有长度为2^j(0<=j<log2(n))的区间上的最大值。
对应每个询问[L,R],设len=R-L,x=log2(len)
则区间[L,R]可分成[L,L+2^x-1]和[R-2^x+1,R],两个区间中间可能会重叠,但绝不会有间隔。
于是[L,R]上的最大值即为MAX(max[L][x],max[R-2^x+1][x])
最小值的做法同法,然后相减即为答案。
#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
const int MAXN = 50005, MAXM = 22;
int maxh[MAXN][MAXM], minh[MAXN][MAXM];
int main() {
int n,q,l,r,x;
while(~scanf("%d%d",&n,&q)) {
for(int i = 0; i < n; i++) {
scanf("%d",&x);
maxh[i][0] = minh[i][0] = x;
}
for(int i = 1; (1 << i ) < n; i++)
for(int j = 0; j + (1 << i) - 1 < n; j++) {
maxh[j][i] = max(maxh[j][i - 1],maxh[j + (1 << (i - 1))][i - 1]);
minh[j][i] = min(minh[j][i - 1],minh[j + (1 << (i - 1))][i - 1]);
}
while(q--) {
scanf("%d%d",&l,&r);
l--;
r--;
x = (int) log2(r - l + 1.0) ;
printf("%d\n",max(maxh[l][x],maxh[r - (1 << x) + 1][x])-min(minh[l][x],minh[r - (1 << x) + 1][x]));
}
}
return 0;
}