版权声明:本文为博主原创文章,顺手点个赞叭~有问题欢迎指出(*╹▽╹*) https://blog.csdn.net/qq_41117236/article/details/89461110
【题面】
【题解】
题意:定义一个区间的价值为区间和*区间最小值,给定一个序列,求子区间的最大价值。
思路:先用ST表维护区间前缀和最大最小值,然后用单调栈跑出以每个值为最小值的最大左边界和右边界,最后对每个值分成负数和正数讨论取可行区间内的最小或最大值。当为负值时,在区间[i,最大右边界]查询最小前缀和,在区间[最大左边界-1,i-1]查询最大前缀和,这样相减能得到最小区间和;当为正值时,在区间[i,最大右边界]查询最大前缀和,在区间[最大左边界-1,i-1]查询最小前缀和,这样相减能得到最大区间和。
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
const ll inf=1e17;
ll a[maxn],sum[maxn];
ll l[maxn],r[maxn];
ll stmx[maxn][23],stmn[maxn][23];
stack <int> stk;
ll askmx(int l,int r)
{
int k=log2(r-l+1);
return max(stmx[l][k],stmx[r-(1<<k)+1][k]);
}
ll askmn(int l,int r)
{
int k=log2(r-l+1);
return min(stmn[l][k],stmn[r-(1<<k)+1][k]);
}
int main()
{
int n; scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+a[i];
//ST表维护前缀和最大最小值
for(int i=0;i<=n;i++)
stmx[i][0]=stmn[i][0]=sum[i];
for(int j=1;(1<<j)<=n+1;j++){
for(int i=0;i+(1<<j)-1<=n;i++){
stmx[i][j]=max(stmx[i][j-1],stmx[i+(1<<(j-1))][j-1]);
stmn[i][j]=min(stmn[i][j-1],stmn[i+(1<<(j-1))][j-1]);
}
}
//单调栈维护以a[i]为最小值的最左最右端点
for(int i=1;i<=n;i++){
while(!stk.empty()&&a[i]<=a[stk.top()])
stk.pop();
if(stk.empty()) l[i]=1;
else l[i]=stk.top()+1;
stk.push(i);
}
while(!stk.empty()) stk.pop();
for(int i=n;i>=1;i--){
while(!stk.empty()&&a[i]<=a[stk.top()])
stk.pop();
if(stk.empty()) r[i]=n;
else r[i]=stk.top()-1;
stk.push(i);
}
//查询
ll ans=-inf;
for(int i=1;i<=n;i++){
if(a[i]<0){
ll R=askmn(i,r[i]);
ll L=askmx(l[i]-1,i-1);
ans=max(ans,(R-L)*a[i]);
}
else if(a[i]>0){
ll R=askmx(i,r[i]);
ll L=askmn(l[i]-1,i-1);
ans=max(ans,(R-L)*a[i]);
}
else ans=max(ans,0ll);
}
printf("%lld\n",ans);
return 0;
}