最大最小

最大最小

题面:给定一个长度为n的正整数数组,q个询问,每个询问给出[a,b],问有多少个字数组,满足最大值最小值在[a,b]之中。

一开始拿到这道题,肯定想的是线段树,但是用ST(倍增算法)把区间最值求出来之后呢?去把所有求出来的最值一一和所有的[a,b]进行比对,肯定也会爆的,所以这道题最关键的地方不是ST,是如何去比对。
但是先给出ST:

void ST(int n) {
    for (int i = 1; i <= n; i++)
        dp[i][0]=a[i];//自己是自己
    for (int j=1;(1<<j)<=n; j++) {
        for (int i=1;i+(1<<j)-1<=n;i++)//倍增往上走 
          {
            dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);//自己本身的最大值即子最大值的最大值,求最小亦然
            f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
          }
    }
}
int RMQ(int l, int r) {
    int k=0;
    while ((1<<(k+1))<=r-l+1) k++;//往上走
    return max(dp[l][k],dp[r-(1<<k)+1][k])-min(f[l][k],f[r-(1<<k)+1][k]);
}

ST并不难,重要的是如何去想这道题呢?

首先问题转化为(<=high的答案数) - (< low的答案数)
也就是说要求出有多少个子数组的和<=一个定值。
枚举左端点,右端点有单调性, two pointer的经典模型。、

这是出题人lzw大魔王自己给出的题解
这是大魔王lzw的代码

ll Solve(int low,int high)//枚举左端点
{
    ll res=0;
    for (int i=1,r=1;i<=n;i++)
    {
        while (r<=n && query(i,r)<low) r++;
        ans1[i]=r;
    }
    for (int i=1,r=1;i<=n;i++)
    {
        while (r+1<=n && query(i,r+1)<=high) r++;
        ans2[i]=r;
    }
    for (int i=1;i<=n;i++) if (ans1[i]<=ans2[i]) res+=ans2[i]-ans1[i]+1;
    return res;
}

往右走,必定是能满足的,最大值最小值必定不变,那么枚举左端点,往右走具有单调性,大小肯定在范围内。
就是这样的
但是要注意大魔王一些关于时间和空间的优化
longlong不要开太多
log的值自己预处理

猜你喜欢

转载自blog.csdn.net/beautiful_cxw/article/details/81036668