描述:https://www.luogu.com.cn/problem/P2422
kkk做了一个人体感觉分析器。每一天,人都有一个感受值Ai,Ai越大,表示人感觉越舒适。在一段时间[i, j]内,人的舒适程度定义为[i, j]中最不舒服的那一天的感受值 * [i, j]中每一天感受值的和。现在给出kkk在连续N天中的感受值,请问,在哪一段时间,kkk感觉最舒适?
先考虑暴力。
怎么暴力?我会枚举区间左右端点
光是枚举左右端点就已经T飞了
我们考虑最小值有多少种情况?
没错,是n种,每个数都可能是最小值。
如果把某个数当成区间最小值的话,那么在保证最小的前提下,区间越大越好
但是问题来了。区间和可以用前缀和优化,那怎么定区间呢?
说白了,我们想找到这个数左边第一个更小的数记作L和右边第一个更小的数记作R
怎么办?单调队列
当压入一个数的时候,会弹出一些更小的数,那么其实弹出的那些数的R值就是本数
同样,当无法从队列里弹出数的时候,那么本数的L值就是那个在队列尾的数
至于一个数的右边或者左边没有更小的数时,我们认为把它们设置为下标为0,n+1
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll n; ll a[100009],q[100009],p[100009]; ll l[100009],r[100009],sumn[100009]; int main() { cin>>n; ll tail=0,head=1; for(int i=1;i<=n;i++) { scanf("%ld",&a[i]); sumn[i]=sumn[i-1]+a[i]; while(tail>=head&&a[i]<q[tail]) { //a[i]是被踢掉元素右边的第一个更小的元素 r[p[tail]]=i; tail--; } l[i]=p[tail];//踢到不能踢为止,那个踢不动的就是左边第一个更小的元素 q[++tail]=a[i],p[tail]=i; } sumn[n+1]=sumn[n]; for(int i=1;i<=n;i++) { if(l[i]==0) l[i]=0; if(r[i]==0) r[i]=n+1; } ll ans=0; for(int i=1;i<=n;i++) ans=max(ans,a[i]*(sumn[r[i]-1]-sumn[l[i]])); cout<<ans; }