题解 P2422 【良好的感觉】
原文链接
一看貌似就是单调队列优化DP...
然后越看越不像...
我们想一下暴力怎么写,枚举一个点i,值为x,找到左边第一个小于x的位置记为l, 同样的找到右边第一个小于x的位置记作r。
于是显然我们的答案就是x*(qzh[r]-qzh[l-1]),qzh指前缀和数组。这样显然是O(n^2)的,我们考虑优化。
因为题目的特性,很容易想到用单调栈维护递增序列。
我们用单调栈存储下标,保证下标对应的值是单调递增的。
于是我们对于一个新位置i,它的值为x,然后弹掉栈顶值比x大的,直到val[stack[top]] < x。
我们把弹出的那些下标的右端点(即右边第一个比它的值小的位置)设为i。
然后对于新插入的i,我们把i的左端点(与上面类似)设为top-1。
考虑这个做法的正确性。
我们的栈里的坐标是单调递增的,值也是单调递增的,我们新插入的值也如果比前面的小,一定是第一个比前面小的数(显然)。
我们弹晚之后剩下的最后一个一定是坐标最大的比x小的数。
所以我们处理完以上之后再O(n)统计一下答案就行了。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
inline int read(){
int res=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
return res;
}
#define ll long long
int n;
int a[100005];
ll qzh[100005];
ll stack[100005], top; //维护下标
int le[100005], ri[100005];
ll ans;
int main()
{
n = read();
for (int i = 1 ; i <= n ; i ++) a[i] = read(), qzh[i] = qzh[i-1] + a[i];
for (int i = 1 ; i <= n ; i ++)
{
while(top and a[stack[top]] >= a[i]) ri[stack[top--]] = i;
le[i] = stack[top];
stack[++top] = i;
}
for (int i = 1 ; i <= n ; i ++) {if (!le[i]) le[i] = 0;if (!ri[i]) ri[i] = n;}
for (int i = 1 ; i <= n ; i ++) ans = max(ans, a[i] * (qzh[ri[i]-1] - qzh[le[i]]));
printf("%lld\n", ans);
return 0;
}