题目:
- 11.46%
- 3000ms
- 262144K
Alice has a magic array. She suggests that the value of a interval is equal to the sum of the values in the interval, multiplied by the smallest value in the interval.
Now she is planning to find the max value of the intervals in her array. Can you help her?
Input
First line contains an integer n(1 \le n \le 5 \times 10 ^5n(1≤n≤5×105).
Second line contains nn integers represent the array a (-10^5 \le a_i \le 10^5)a(−105≤ai≤105).
Output
One line contains an integer represent the answer of the array.
样例输入复制
5
1 2 3 4 5
样例输出复制
36
题意:给你一个序列,每一个区间有一个贡献值,等于这个区间中的最小值 × 这段区间和,求贡献值的最大值。
单调栈:可以找到左边或者右边第一个大于/小于自己的数。时间复杂度为O(N);
思路:枚举每个数作为最小值,这个数作为最小值影响的最大的区间,可以用单调栈求出。
不会的请看视频:https://www.bilibili.com/video/av23189029?from=search&seid=18026655336668737255
当求出每个数作为最小值影响的区间时
当这个数a[i]>=0时,a[i] 为正数,a[i]还是最小,所以 当前最为最小值区间的贡献值为a[i] * 影响区间的区间和;
当a[i] < 0 时,在a[i]作为最小值影响区间中,包含a[i]的区间的最小值。但是要怎么求这个区间的最小值呢,
首先我们先求出所有数的前缀和,假设以a[i]为最小值影响的区间的左、右端点分别为l,r,先求出 l~i 下标的前缀和的最大值 k1,再求出i~r下标的前缀和的最小值 k2,k2 - k1就是所要找的区间的最小值。这里需要注意,k1为负数时,要减去max(k1,sum[l-1]),求最值的话用 rmq维护前缀和就行
结论1:在一段区间[1,n] 中求包含这个数的区间的最小值:先求出所有数前缀和,再求出 1~i 下标的前缀和的最大值 k1,再求出i~n下标的前缀和的最小值 k2,k2 - k1就是所要找的包含这个数区间的最小值。
结论2:在一段区间[1,n]中求包含这个数的区间和最大值,预处理所有数的前缀和,再求出1~i下标的前缀和的最小值k1 和 i~n下标的最大值k2,k2 - k1 就是所有要找的包含这个数的区间的最大值。
案例1
-11 2 3 -5 -4
-11 -9 -6 -11 -15 看-5,-15 - max(-11,-6) = -9. (-5) *(-9) = 40.
案例2:
4 -5 -2 1 -2 -3 -2 -1 4
4 -1 -3 -2 -4 -7 -9 -10 -6 看a[i] = -3时的状态
代码:
#include<bits/stdc++.h>
using namespace std;
const int Max = 5e5;
#define ll long long
struct node
{
int val,id;
int l,r;
}stu[Max+10];
stack<node> s;
int n,Log[Max+10],c[30];
ll sum[Max+10],dpM[Max+10][21],dpN[Max+10][21];
void D_R() // 单调栈,求右边第一个小于它的数;
{
for(int i = 1;i<=n;i++)
{
stu[i].r = n+1;
while(s.size()&&s.top().val>stu[i].val)
{
node tt = s.top();
s.pop();
stu[tt.id].r = i;
}
s.push(stu[i]);
}
while(s.size())
s.pop();
}
void D_L()
{
for(int i = n;i>=1;i--)
{
stu[i].l = 0;
while(s.size()&&s.top().val>stu[i].val)
{
node tt = s.top();
s.pop();
stu[tt.id].l = i;
}
s.push(stu[i]);
}
while(s.size())
s.pop();
}
void rqm()
{
for(int j = 1;j<=Log[n];j++)
for(int i = 1;i+c[j]-1<=n;i++){
dpM[i][j] = max(dpM[i][j-1],dpM[i+c[j-1]][j-1]);
dpN[i][j] = min(dpN[i][j-1],dpN[i+c[j-1]][j-1]);
}
}
int main()
{
c[0] = 1;
for(int i = 1;i<=21;i++)
c[i] = c[i-1] * 2;
scanf("%d",&n);
Log[0] = -1;
for(int i = 1;i<=n;i++){
scanf("%d",&stu[i].val);
Log[i] = Log[i>>1] + 1;
stu[i].id = i;
sum[i] = sum[i-1] + (ll)stu[i].val;
dpM[i][0] = dpN[i][0] = sum[i];
}
rqm();
D_R();
D_L();
ll ans = -0x3f3f3f3f3f3f3f;
for(int i = 1;i<=n;i++){
if(stu[i].val>=0)
ans = max((ll)stu[i].val*(sum[stu[i].r-1]-sum[stu[i].l]),ans);
else{
int l = stu[i].l+1,r = stu[i].r - 1;
int t1 = Log[i-l+1],t2 = Log[r-i+1];
ll k1 = max(dpM[l][t1],dpM[i-c[t1]][t1]);
ll k2 = min(dpN[i][t2],dpN[r-c[t2]][t2]);
if(k1>=0) ans = max(ans,(ll)stu[i].val * (k2-k1));
else ans = max(ans,(ll)stu[i].val*(k2-max(sum[stu[i].l],k1)));
// 减去 max(边界值,k1)两者的较大值。
}
}
printf("%lld\n",ans);
return 0;
}