题目大意:

给你一串数,50,000个,询问200,000个区间,每次询问输出该区间最大值与最小值的差

分析:

这个因为不用修改,只需要查询,其实用树状数组也是可以的我觉得,但是因为还是有一个log50000,说不定真的会超时,所以还是选择用他给的这个O(1)复杂度的算法吧。

关于st算法:

就是首先预处理:

dp [ i ] [ j ] 表示 从第 i 个数开始 向后数 2j1 个数之后这之间所有的数的最大值(最小值),既, dp [ i ] [ j ] 表示区间 [ i,i+2j1 ] 中 a [ ] 的最大值(最小值)。那么,通过状态转移方程:dp [ i ] [ j ] = max { dp [ i ] [ j-1] , dp [ i+2j1 ] [ j-1 ] } 即可确定所有的 i 以及在可选区间内所有的连续的 j 所对应的 dp [ i ] [ j ] 。时间复杂度O(nlogn)。

关于查询:

查询区间 [ a , b ] 就是 max { dp [ a ] [ k ] , dp [ b- 2k +1 ] [ b ] } ; 其中 k=log(b-a+1) 。

代码:

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>

using namespace std;
#define maxn 50500

int n,q;
int dmax[maxn][30];
int dmin[maxn][30];
int a[maxn];

void build()
{
    for(int i=1;i<=n;i++)dmax[i][0]=a[i];  
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
            dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]);
        }
    } 
    for(int i=1;i<=n;i++)dmin[i][0]=a[i];  
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
            dmin[i][j]=min(dmin[i][j-1],dmin[i+(1<<(j-1))][j-1]);  
        }
    }
}

int getmax(int l,int r)
{
    int k=0;
    while((1<<(k+1))<=r-l+1)k++;
    return max(dmax[l][k],dmax[r-(1<<k)+1][k]);
}
int getmin(int l,int r)
{  
    int k=0;  
    while((1<<(k+1))<=r-l+1)k++;  
    return min(dmin[l][k],dmin[r-(1<<k)+1][k]);  
}
int main()
{
    while(cin>>n>>q)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        build();
        int a,b;
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d",&a,&b);
            printf("%d\n",getmax(a,b)-getmin(a,b));
        }
    }
}

ST算法的xcx理解:

首先用一种很暴力的方法,如果我求出了任意一个区间的最大值,然后存起来,随查随用,那么查询就是完美的O(1)时间复杂度。但是这样预处理就太慢了,用动态规划应该也需要 O(n2) 的时间复杂度。然后我就想,能不能对于每个区间起点,我只存有限的几个特定的区间终点的最大值。用二分的思想,我就存logn个。那么现在我就得到了以任意一个点为起点的若干(logn)个特定的区间的最大值。那么现在对于任意一个我想要查询的区间 [ a , b ] ,我现在肯定能知道以 a 为起点的区间终点不超过b的最长的存好的区间的最大值。然后这个区间终点的位置肯定在 ab 两点终点靠近 b 点的位置,因为否则的话,一定存在更大的存好的区间,其区间终点不超过b。然后找以b终点的区间,存好的区间里面,肯定有一个区间起点位置在 ab 两点终点靠 a 一侧的区间。这样我就把求区间 [ a , b ] 的最大值,转换成了求两个已经存好的有交叉部分的子区间的最大值的。

后注:

这波魔板代码抄的真是爽,写了一下午比赛,ac4题,实在懒得想这个代码的细节了,回来要自己再好好写一道rmq的题。