题目链接:https://nanti.jisuanke.com/t/38228
给一个长度为n的序列,求出最大的区间值。区间值的定义为:区间和*区间最小值。
更简单的思路及实现:预处理前缀和,st表或者线段树记录区间最大前缀和与最小前缀和,单调栈跑出区间,以该点为中心点,若是正数,则求区间[x,R]内的最大值,[L,x]内的最小值,负数反一下就可以。
预处理前缀和,紧靠区间最左端的最小值,紧靠区间最右端的最小值。
然后枚举每个位置,将该数作为一个区间的最小值,那么该区间的左右端点可以通过单调栈找到。
然后对于该数x,假设该位置为i,区间左端点为L,右端点为R。
- 若x为正数,则[L,R]这一段的元素全取,因为[L,R]上的元素都是大于等于x的,故都为正数,全取。
- 否则,找[L,i],[i,R]中紧靠i的最小子段和。
那么我们如何计算呢。
紧靠左端的区间最小值:lmin[], 右端也是同理。
那么线段树上搜索[L,R]的时候分为两部分,这里给出[i,R]的,另一半也是同理。
对于每个能递归到的负数x的[i,R]的子区间[l,r],他的和就是紧靠l的lmin数组加上[i~l-1]的前缀和。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<queue>
#include<stack>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f3f3f3f3f;
const int maxn=5e5+7;
ll sum[maxn<<2|1];
ll lmin[maxn<<2|1];
ll rmin[maxn<<2|1];
ll suml[maxn];//前缀和;
void pushup(int k){
sum[k]=sum[k<<1]+sum[k<<1|1];
lmin[k]=min(lmin[k<<1],sum[k<<1]+lmin[k<<1|1]);
rmin[k]=min(sum[k<<1|1]+rmin[k<<1],rmin[k<<1|1]);
}
int a[maxn];
void build(int l,int r,int k){
if(l==r){
sum[k]=lmin[k]=rmin[k]=a[l];
return ;
}
int mid=(l+r)>>1;
build(l,mid,k<<1);
build(mid+1,r,k<<1|1);
pushup(k);
}
int x;
ll myfindL(int L,int R,int l,int r,int k){//找[x,R];
if(l>=L&&r<=R){
return suml[l-1]-suml[x-1]+lmin[k];
}
int mid=(l+r)>>1;
ll res=inf;
if(L<=mid){
res=min(res,myfindL(L,R,l,mid,k<<1));
}
if(R>mid){
res=min(res,myfindL(L,R,mid+1,r,k<<1|1));
}
return res;
}
ll myfindR(int L,int R,int l,int r,int k){//[L,x];
if(l>=L&&r<=R){
return suml[x]-suml[r]+rmin[k];
}
int mid=(l+r)>>1;
ll res=inf;
if(L<=mid){
res=min(res,myfindR(L,R,l,mid,k<<1));
}
if(R>mid){
res=min(res,myfindR(L,R,mid+1,r,k<<1|1));
}
return res;
}
int l[maxn],r[maxn];
stack<int>s;
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
suml[i]=suml[i-1]+a[i];
}
build(1,n,1);
for(int i=1;i<=n;++i){
while(s.size()&&a[s.top()]>=a[i]) s.pop();
if(s.empty()) l[i]=0;
else l[i]=s.top();
s.push(i);
}
while(!s.empty()) s.pop();
for(int i=n;i>=1;--i){
while(s.size()&&a[s.top()]>=a[i]) s.pop();
if(s.empty()) r[i]=n+1;
else r[i]=s.top();
s.push(i);
}
ll res=-inf;
ll mutl;
for(int i=1;i<=n;++i){
mutl=a[i];
x=i;
int ll=l[i]+1;
int rr=r[i]-1;
//cout<<"[ "<<ll<<" , "<<rr<<" ]"<<endl;
if(a[i]>=0){
res=max(res,(suml[rr]-suml[ll-1])*a[i]);
}
else{
res=max(res,(myfindL(i,rr,1,n,1)+myfindR(ll,i,1,n,1)-a[i])*a[i]);
//cout<<myfindL(i,rr,1,n,1)<<endl;
//cout<<myfindR(ll,i,1,n,1)<<endl;
}
}
printf("%lld\n",res);
return 0;
}