作用:
给定一个数列 ai O(nlogn) 预处理 O(1) 查询区间最值
实现:
定义f(i,j) 为ai开始,包括ai的连续2^j个元素的最值
下面以最大值为例:
f(i,j)表示max{ak}(k∈[i,i+2^j)).
利用倍增:
f[i][j]=
{aimax(f[i][j−1],f[i+2j−1][j−1])j=0j>0{aij=0max(f[i][j−1],f[i+2j−1][j−1])j>0
区间([L,R])求值:
把区间看做[L,L+2^j-1]和[R-2^j+1,R]两部分
虽然有重叠(或者说相交)但可以忽略(因为要求最值)
其中 j=⌊log₂(R−L+1)⌋
所以 f[i][j]=max(f[L][j],f[R-2^j+1][j]);
code:
//By Menteur_Hxy
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<ctime>
using namespace std;
const int MAX=2000;
int n,m,x,y;
int a[MAX+5],lg[MAX+5],maxn[MAX+5][MAX+5];
void solve(int *x) {
for(int i=1;i<=n;i++) {
lg[i]=lg[i-1];
if(1<<lg[i-1]+1==i) lg[i]++;
//lg[i]=((i & (i-1))==0) ? lg[i-1]+1 : lg[i-1];
}
for(int j=1;j<=lg[n];j++)
for(int i=1;i+(1<<j)-1<=n;i++)
maxn[i][j]=max(maxn[i][j-1],maxn[i+1<<(j-1)][j-1]);
}
int askm(int l,int r) {
int k=lg[r-l+1];
return max(maxn[l][k],maxn[r-(1<<k)+1][k]);
}
int main() {
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
maxn[i][0]=a[i];
}
solve(a);
for(int i=1;i<=m;i++) {
scanf("%d %d",&x,&y);
printf("%d
",askm(x,y));
}
return 0;
}