嘘~ 正在从服务器偷取页面 . . .

洛谷P2422 良好的感觉 题解


洛谷P2422 良好的感觉 题解

题目链接:P2422 良好的感觉

题意

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

对于 $100\%$ 的数据,$1\le N\le 100000$,$1\le \texttt{感受值}\le 1000000$。

题意简化一下就是

找到一个区间,使得区间最小值乘以区间的和最大

考虑每个最小值对区间的贡献

设当前的数为 $a_i$ ,

它能成为最小值的区间 $[l,r]$ 满足 $l\in [j+1,i],r\in [i,k-1]$

其中 $j$ 是所有 $a_j <a_i$ 中最大的 $j$ ,$k$ 是所有 $a_k < a_i$ 中最小的 $k$

设 $f_i$ 表示前 $i$ 个数中的最大答案

这里的 $j,k$ 和上面同义,$S_i$ 为前缀和,即 $S_i = \sum_{j=1}^{i}a_j$

于是用单调栈维护最小值,从左往右扫,就能把左半部分搞定了

那么右半部分怎么搞呢?

不难发现 $a_k$ 一定是第一个加入单调栈以后把 $a_i$ 踢出去的数

我们只要在踢出 $a_i$ 的时候把 $i$ 到 $k-1$ 的贡献给加到 $f_i$ 上就好了

注意最后一定要把 $a_{n+1}$ 设为一个极小值,使其能把前面所有的踢出去

时间复杂度 $O(n)$

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e5+15)

int n,top,a[N],stk[N],f[N],sum[N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n;
    for(int i=1; i<=n; i++)
        cin >> a[i];
    a[++n]=0;
    for(int i=1; i<=n; i++)
    {
        sum[i]=sum[i-1]+a[i];
        while(a[stk[top]]>a[i])
        {
            f[stk[top]]+=sum[i-1]-sum[stk[top]];
            --top;
        }
        f[i]=sum[i]-sum[stk[top]];
        stk[++top]=i;
    }
    int res=0;
    for(int i=1; i<n; i++)
        res=max(res,f[i]*a[i]);
    cout << res << '\n';
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录