P2422 良好的感觉(两头单调)

描述: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;
}

猜你喜欢

转载自www.cnblogs.com/iss-ue/p/12531554.html