Luogu2422 良好的感觉 (单调栈)

题目描述

kkk做了一个人体感觉分析器。每一天,人都有一个感受值Ai,Ai越大,表示人感觉越舒适。在一段时间[i, j]内,人的舒适程度定义为[i, j]中最不舒服的那一天的感受值 * [i, j]中每一天感受值的和。现在给出kkk在连续N天中的感受值,请问,在哪一段时间,kkk感觉最舒适?

输入格式

第一行为N,代表数据记录的天数

第二行N个整数,代表每一天的感受值

输出格式

一行,表示在最舒适的一段时间中的感受值。

输入输出样例

输入 #1
6
3 1 6 4 5 2
输出 #1
60

————————————————————————————————————————————

在考第十八次CSP认证之前想找几道单调队列和单调栈的题来复习下这两个算法(估计CCF也不会给CSP认证出这样的题。

本题不难得出暴力 $ O(n^2) $ 的做法,可以使用ST表或者前缀和预处理后枚举区间起点和终点来计算每个区间的权值并取其最大值为答案,但是这样只能通过70%的数据。

另外还有一种复杂度不变的做法就是考虑对于某天的感受值 $ Ai $ 为所求区间的最小值,我们需要使用两个代表左右边界的变量以 $ i $ 为起点扩展这个区间,直到前方值为一个严格小于 $ Ai $ 的值便停止,由于本题中每个感受值都是正数,我们可以不难证明这种做法的正确性:在区间最小值和其位置确定的的情况下,覆盖的正数区间越大,得出的答案越大。同样,由于需要枚举每一个感受值为某区间的最小值,所以限于算法复杂度无法通过这道题。

所以我们可以考虑一种利用单调栈的优化做法,考虑到我们仅仅关心某个感受值为最小值适在其左右端第一个严格小于区间最小值的值所在的位置,所以我们可以用一个严格单调递增的栈(栈顶最大)来维护这种关系。在某个感受值 $ Ai $ 被确定为最小值并准备入栈时,我们应该从栈顶开始弹出每一个不小于 $ Ai $ 的值(在记录栈内每个值的同时记录该值的位置),并将这些被弹出的值的位置的 $ Rig $ 数组标记为 $ i $,以表示 $ Ai $ 是它们右边的第一个小于它的数。将弹出后得到的栈顶的数 $ Aj $ 即为 $ i $ 左边第一个小于它的数,将 $ Rig[i] $ 标记为 $ j $ ,由单调栈的性质可知对于 $ lef $ 和 $ rig $ 数组的处理是准确的。

代码如下:

#include <bits/stdc++.h>
#define ll long long
#define MAXN 100007
using namespace std;
struct Point { int id,num; }sta[MAXN]; 
int n,tot; ll ans,sum[MAXN];
int a[MAXN],lef[MAXN],rig[MAXN];
inline int read() {
    int w=0,X=0; char ch=0;
    while (!isdigit(ch)) w|=ch=='-',ch=getchar();
    while (isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X; 
}
int main() {
    n=read();
    for (int i=1;i<=n;i++) {
        a[i]=read(),sum[i]=sum[i-1]+a[i];
        while (tot && sta[tot].num>=a[i]) rig[sta[tot--].id]=i;
        lef[i]=tot?sta[tot].id:0;
        sta[++tot]=(Point){i,a[i]};
    }
    for (int i=1;i<=n;i++) {
        if (!rig[i]) rig[i]=n+1;
        ans=max(ans,a[i]*(sum[rig[i]-1]-sum[lef[i]]));
    }
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zhwer/p/12031254.html