ST算法O(nlogn)預處理,O(1)的查詢指定區間的最值(以最小值為例)
基本上是把待求區間[l,r]分為兩段長為len的區間
左邊一段為[l,l+len-1],右邊一段為[r-len+1,r]
len必須使得兩段區間覆蓋待求區間
設所求數組為w
那么,所求最小值就是兩個區間的最小值間的最小值
即min(min{w[i],l<=i<=l+len-1},min{w[j],r-len+1<=j<=r})
若都在預先處理中先求得兩個區間的最小值
則每次查詢的復雜度都是O(1)
---
對len做一個限制:只能為2的冪
在預處理中求出所有mi[b][t] : 以b為起點,長為2^t的區間的最小值.
則求解min(min{w[i],l<=i<=l+len-1},min{w[j],r-len+1<=j<=r})
就變成min(mi[l][t],mi[r-2^t+1][r]),其中t可以由此得出,以保證兩段區間可以覆蓋待求區間:
t=ln(r-l+1)/ln(2)
---
可以看到mi[b][t]=min(mi[b][t-1],mi[b+2^(t-1)-1][t-1])
特別地對于所有mi[i][0],其值都是w[i];
由此自底向上推出所有的mi[b][t]
mi大小為n*logn,預處理時間復雜度為O(nlogn),查詢時間復雜度為O(1)
#include <iostream>
?#include <math.h>
?#define max(a,b) ((a>b)?a:b)
?#define min(a,b) (a<b?a:b)
?
using namespace std;
?
const int maxn=50001;
?int h[maxn];
?int mx[maxn][16],mn[maxn][16];
int n,q;
?
?void rmq_init()
?{
???? int i,j;
???? for(j=1;j<=n;j++) mx[j][0]=mn[j][0]=h[j];
???? int m=floor(log((double)n)/log(2.0));
???? for(i=1;i<=m;i++){
???????? for(j=n;j>0;j--){
???????????? mx[j][i]=mx[j][i-1];
???????????? if(j+(1<<(i-1))<=n) mx[j][i]=max(mx[j][i],mx[j+(1<<(i-1))][i-1]);
???????? }
??? }
??? for(i=1;i<=m;i++){
???????? for(j=n;j>0;j--){
??????????? mn[j][i]=mn[j][i-1];
???????????? if(j+(1<<(i-1))<=n) mn[j][i]=min(mn[j][i],mn[j+(1<<(i-1))][i-1]);
???????? }
???? }
?}
?
?int rmq(int l,int r)
?{
???? int m=floor(log((double)(r-l+1))/log(2.0));
??? int a=max(mx[l][m],mx[r-(1<<m)+1][m]);
???? int b=min(mn[l][m],mn[r-(1<<m)+1][m]);
??? return a-b;??
?}
?
?int main()
?{
???? int i,l,r;
???? scanf("%d%d",&n,&q);
???? for(i=1;i<=n;i++) scanf("%d",&h[i]);
???? rmq_init();
???? for(i=0;i<q;i++){
???????? scanf("%d%d",&l,&r);
???????? printf("%d\n",rmq(l,r));
???? }
?}