#10119. 「一本通 4.2 例 1」数列区间最大值 & P1816 忠诚(ST算法)

  • https://loj.ac/problem/10119 & https://www.luogu.org/problemnew/show/P1816

  • 时间复杂度:O(nlogn)+O(m);(n为个数,m为问题数)

  • 思路:详见信息学奥赛一本通;

  1. 预处理:ST算法实际是DP;a[MAXN]存数,f[i][j](f[MAXN][21])表示a[i]~a[i+2的j次方-1]范围内最大值(即a[i]为起点连续2的j次方个数的最大值);从中间平均分成两部分,每部分元素刚好2的j-1次方个,即,f[i][j]分为f[i][j-1]和f[i+2的j-1次方][j-1]。故得到状态转移方程f[i][j]=max(f[i][j-1],f[i+2的j-1次方][j-1]),边界条件f[i][0]=a[i]

  2. 询问:询问[li,ri]间最大值(即max,最小值即min),先求出x满足2的x次方<=ri-li+1,那么区间[li,ri]=[li,li+2的x次方-1]∩[ri-2的x次方+1,ri],两个区间元素均为2的x次方个,所以[li,ri]最大值为max(f[li][x],f[ri-2的x次方+1][x])(∵有交集)。直接给表达式:k=log2(y-x+1);ans=max(f[x][k],f[y-2的k次方+1][k])

  3. 技巧(待证明):log2函数效率低,故可用O(N)递推预处理出1~N这N种区间长度各自对应的k值,具体操作:lg[d]数组表示log2d向下取整,则lg[d]=lg[d/2]+1;

  • 代码:最大值max最小值min

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=1e6+2;
    int n,m;
    int a[MAXN],lg[MAXN],f[MAXN][21];
    int main(){
        cin>>n>>m;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]); //存数
        lg[0]=-1;
        for(int i=1;i<=n;i++)
            f[i][0]=a[i],lg[i]=lg[i>>1]+1; //边界处理+技巧lg数组
        for(int i=1;i<=20;i++) //一般最大到2的20次方即可
          for(int j=1;j+(1<<i)-1<=n;j++) //j+(1<<i)-1<=n即区间边界不可超过n;1<<i即2的i次方 
            f[j][i]=max(f[j][i-1],f[j+(1<<i-1)][i-1]); //状态转移方程f[i][j]=max(f[i][j-1],f[i+2的j-1次方][j-1])
        int x,y;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            int s=lg[y-x+1]; //技巧lg数组 
            printf("%d\n",max(f[x][s],f[y-(1<<s)+1][s])); //max(f[li][x],f[ri-2的x次方+1][x])(∵有交集) 
        }
        return 0;
    }

     

     

     

posted @ 2019-01-25 11:31  jiansong!  阅读(533)  评论(0编辑  收藏  举报