题意
给定一序列,求其一个子段,满足该子段的和减去其最大值的结果尽可能大。(nleq 10^5)
题解
考虑每个数作为最大值时的最大答案。每个数作为最大值时区间左右端点的范围可以单调栈预处理。于是每次便要查询一段内的最小前缀和和一段内的最大前缀和。ST 表可做(感觉也可以在单调栈的时候一起处理?)。
代码:
#include<bits/stdc++.h>
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
const int N=1e5+10,L=18;
int a[N],l[N],r[N],s[N],n;
int smin[L][N],smax[L][N],l2[N];
void initst(){
for(int i=0;i<=n;i++)smin[0][i]=smax[0][i]=s[i];
for(int i=1;i<L;i++){
for(int j=0;j<=n-(1<<i)+1;j++){
smin[i][j]=min(smin[i-1][j],smin[i-1][j+(1<<i-1)]);
smax[i][j]=max(smax[i-1][j],smax[i-1][j+(1<<i-1)]);
}
}
}
int qmin(int l,int r){
++r;
int t=l2[r-l];
return min(smin[t][l],smin[t][r-(1<<t)]);
}
int qmax(int l,int r){
++r;
int t=l2[r-l];
return max(smax[t][l],smax[t][r-(1<<t)]);
}
int main(){
n=getint();
for(int i=1;i<=n;i++)a[i]=getint(),s[i]=s[i-1]+a[i];
for(int i=2;i<=n;i++)l2[i]=l2[i>>1]+1;
initst();
a[0]=a[n+1]=0x7f7f7f7f;
stack<int>stk;
stk.push(0);
for(int i=1;i<=n;i++){
while(a[stk.top()]<=a[i])stk.pop();
l[i]=stk.top();
stk.push(i);
}
while(stk.size())stk.pop();
stk.push(n+1);
for(int i=n;i>=1;--i){
while(a[stk.top()]<=a[i])stk.pop();
r[i]=stk.top()-1;
stk.push(i);
}
int ans=0;
for(int i=1;i<=n;i++){
int s1=qmin(l[i],i-1),s2=qmax(i,r[i]);
ans=max(ans,s[i-1]-s1 + s2-s[i]);
}
cout<<ans;
}