RMQ问题为求区间最值的问题
线段树可以在O(logN)的时间复杂度内完成询问操作。
但是ST算法可以在常数时间内完成询问操作
ST算法:基于动态规划求区间最值的算法。
分为预处理和查询两部分
预处理:定义 F[i][j] 为从 i开始到 i+2^j-1 区间内的最值 , 我们可以讲这段2^j的区间分成两部分长度都为2^(j-1)的相同区间
区间1 为 i.....i+2^(j-1)-1 区间2为 i+2^(j-1).....i+2^j-1
那么可以得到 F[i][j] =Max( F[i][j-1],F[i+2^(j-1)][j-1],边界条件为F[i][0]=A[i].
由于大的区间是由小的区间得到的,所以预处理时必须按区间长度递增的顺序递推出F[i][j].
查询:求区间[ i , j ]的最值 令 d=(int) log2( j-i+1)
我们取靠i的长度为2^d区间 以及靠j的2^d区间内的最大值 ,两个区间内可以存在公共部分
则i,j max= Max ( F[i][d] ,F[j-2^d+1,d])
给定一个数列,输出访问区间的最大值和最小值的差
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cmath>
#include<string>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
ll a[50001];
ll maxn[50001][20];
ll minn[50001][20];
ll n;
//dp+二分
//预处理 RMQ
void init(){
for(int i=1;i<=n;i++){
maxn[i][0]=minn[i][0]=a[i];
}
for(int j=1;(1<<j)<=n;j++){//枚举区间长度
for(int i=1;i+(1<<j)-1<=n;i++){//枚举区间左端点
//max(区间[i,i+(1<<j)-1]的左半区间[i,i+(1<<(j-1))-1]即maxn[i][j-1],区间[i,i+(1<<j)-1]的左半区间[i+(1<<(j-1)),i+(1<<j)-1],即maxn[i+(1<<(j-1))][j-1])
maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);
minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
}
}
}
ll solve(ll x,ll y){
ll k=(ll)(log(y-x+1)/log(2));
//以上k的求法保证 maxn[x][k]和maxn[y-(1<<k)+1][k]有交集,同理。。。。
return max(maxn[x][k],maxn[y-(1<<k)+1][k])-min(minn[x][k],minn[y-(1<<k)+1][k]);
}
int main(){
ll q,x,y;
scanf("%lld %lld",&n,&q);
for(int i=1;i<=n;i++){
scanf("%lld",a+i);
}
init();//预处理
while(q--){
scanf("%lld %lld",&x,&y);
printf("%lld\n",solve(x,y));
}
return 0;
}