洛谷P2422 良好的感觉 题解

洛谷P2422 良好的感觉 题解

题目链接:P2422 良好的感觉

题意

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

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

题意简化一下就是

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

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

设当前的数为 a i a_i ai

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

其中 j j j 是所有 a j < a i a_j <a_i aj<ai 中最大的 j j j k k k 是所有 a k < a i a_k < a_i ak<ai 中最小的 k k k

f i f_i fi 表示前 i i i 个数中的最大答案
f i = ( f j + S i − S j ) + ( f k + S k − 1 − S i ) f_i = (f_j + S_i-S_j)+(f_k+S_{k-1}-S_i) fi=(fj+SiSj)+(fk+Sk1Si)
这里的 j , k j,k j,k 和上面同义, S i S_i Si 为前缀和,即 S i = ∑ j = 1 i a j S_i = \sum_{j=1}^{i}a_j Si=j=1iaj

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

那么右半部分怎么搞呢?

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

我们只要在踢出 a i a_i ai 的时候把 i i i k − 1 k-1 k1 的贡献给加到 f i f_i fi 上就好了

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

时间复杂度 O ( n ) O(n) 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;
}

转载请说明出处

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值