【CF-1359 D. Yet Another Yet Another Task】 ST表+单调队列

D. Yet Another Yet Another Task

题意

给出一个长度为\(n\)的整数数组,让找出一个连续子序列,它的和减去最大值是最大的。

思路

我写的代码有点多了。。。

ST表+单调队列。

因为是减去最大值,所以我们可以枚举每个数字作为最大值。

左边第一个大于它的数字到右边第一个大于它的数字,这个开区间所有包含最大值的

子区间,都可以被选择。用单调队列求出来左边第一个大于它的位置,右边同理。

那么在左边选择一个前缀和最小的,右边选择一个前缀和最大的,计算出区间和之后减去当前值。

使用ST表维护前缀和。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
typedef long long ll;
typedef unsigned long long ull;

int arr[N],pre[N],maxn[N][20],minn[N][20];
int lmax[N],rmax[N];
int que[N],h,t;
void rmq(int n)
{
    for(int i=0;i<=n;i++) minn[i][0]=maxn[i][0]=pre[i];
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=0;i+j-1<=n;i++)
        {
            maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);
            minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
        }
    }
}
int q1(int l,int r)
{
    int k=0,dis=r-l+1;
    while((1<<(k+1))<=dis) ++k;
    return max(maxn[l][k],maxn[r-(1<<k)+1][k]);
}
int q2(int l,int r)
{
    int k=0,dis=r-l+1;
    while((1<<(k+1))<=dis) ++k;
    return min(minn[l][k],minn[r-(1<<k)+1][k]);
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&arr[i]);
        pre[i]=pre[i-1]+arr[i];
    }
    rmq(n);
    h=1,t=0;
    for(int i=1;i<=n;i++)
    {
        while(h<=t&&arr[que[t]]<=arr[i]) t--;
        if(h<=t) lmax[i]=que[t];
        else lmax[i]=0;
        que[++t]=i;
    }
    h=1,t=0;
    for(int i=n;i;i--)
    {
        while(h<=t&&arr[i]>=arr[que[t]]) t--;
        if(h<=t) rmax[i]=que[t]-1;
        else rmax[i]=n;
        que[++t]=i;
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        int l=q2(lmax[i],i-1),r=q1(i,rmax[i]);
        ans=max(ans,r-l-arr[i]);
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2020-05-29 22:21  Valk3  阅读(212)  评论(0编辑  收藏  举报