Live2D

[OI学习笔记]st表

用来查询区间最值(区间和,差等要处理重复部分)

下面均以以最大值为例

初始化 0(nlogn)

设st[i][k]为下标i开始的2k个元素的最值

则: st[i][k]=max{st[i][k-1],st[i+2k-1][k-1]}

即区间[i,i+2k -1]的前一半和后一半的最值取最大

查询 O(1)

对于区间[l,r],区间长度len=r-l+1;找到满足2p <=len的最大p

最大值ans=max{st[l][p],st[r-2p +1][p]} 

显然,当2p !=len时,区间[l,l+p-1]和[r-2p +1][r]有重叠

这也是为什么前面说区间和,差等要处理重复部分。

2p <=len的p的最大值p <=log2len

p=⌊log2len⌋  (下取整)

代码:

(注意第12行下标范围是i+(1<<k)-1<=n)

 

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;

int st[100010][21],n;//st的第二维取到log2(10010)即可 

void init(){
    for(int k=1;(1<<k)<=n;k++)
        for(int i=1;i+(1<<k)-1<=n;i++)
            st[i][k]=max(st[i][k-1],st[i+(1<<(k-1))][k-1]);
}

int ask(int l,int r){
    int len=r-l+1;
    //int p=(int)log((double)(len))/log(2.0);//满足2^p<=r-l+1的最大p
    int p=log2(len);
    return max(st[l][p],st[r-(1<<p)+1][p]); 
}

//以最大值为例 
int main(){
    int m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&st[i][0]);
    init();
    for(int i=1;i<=m;i++){
        int l,r;
        scanf("%d%d",&l,&r);
        int ans=ask(l,r);
        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2019-02-14 17:18  SHGEEK  阅读(137)  评论(2编辑  收藏  举报