洛谷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\) 个数中的最大答案 \[ f_i = (f_j + S_i-S_j)+(f_k+S_{k-1}-S_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;
}