P2422 良好的感觉 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
kkk 做了一个人体感觉分析器。每一天,人都有一个感受值 , 越大,表示人感觉越舒适。在一段时间 内,人的舒适程度定义为 中最不舒服的那一天的感受值 中每一天感受值的和。现在给出 kkk 在连续 天中的感受值,请问,在哪一段时间,kkk 感觉最舒适?
输入格式
第一行为 ,代表数据记录的天数。
第二行 个整数,代表每一天的感受值。
输出格式
一行,表示在最舒适的一段时间中的感受值。
样例 #1
样例输入 #1
6
3 1 6 4 5 2
样例输出 #1
60
提示
kkk 最开心的一段时间是第 天到第 天,开心值:。
对于 的数据,。
对于 的数据,。
对于 的数据,,。
思路
数据范围规定没有负数,所以前缀和绝对是会越来越大的,应该优先考虑那个最不舒服的值。
可以发现这样一个性质:
对于任意一个点 , 找其左边第一个 的 , 右边第一个 的 。此时, 在 区间内, 最优解就是 , 也就是说, 最终答案只会在这样的区间中存在。
我们只需要发现一种方法能快速枚举这样的区间就行。
那么有没有一种方法能在 的复杂度得到所有满足条件的区间呢? 使用单调栈就行, 当新元素 时, 弹出 并入栈 。再定义 为 所对应的最大区间和。
此时可以发现, 当出现满足 的 时, 右边的第一个小于值就是 , 此时加上 的右区间和 。
而它的左区间就是在还未加入队列前, 设此时为 , 此时因为在加入队列前会先更新栈尾, 把 的都给弹飞, 那么留下来的就是最小值, 满足 。
更新完栈尾后, 当前的 所对应的左区间和就是 。
因此, 对于每一个可能的最小值 , 其能达到的区间和就是 , 只会加这两次。
单调队列/栈的题目真烦啊啊啊啊啊啊啊啊
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL a[N], s[N], q[N], f[N];
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
n++, a[n] = 0, s[n] = s[n - 1];
int tail = 0;
for (int i = 1; i <= n; i++)
{
while (a[q[tail]] > a[i])
{
f[q[tail]] += s[i - 1] - s[q[tail]];
tail--;
}
f[i] = s[i] - s[q[tail]];
q[++tail] = i;
}
LL res = 0;
for (int i = 1; i < n; i++)
res = max(res, f[i] * a[i]);
cout << res << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
return 0;
}