【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;
}