P1714 切蛋糕 - 单调队列

新博客地址:https://gyrojeff.top,欢迎访问! 本文为博客自动同步文章,为了更好的阅读体验,建议您移步至我的博客👇

本文标题:P1714 切蛋糕 - 单调队列

文章作者:gyro永不抽风

发布时间:2020年09月20日 - 18:09

最后更新:2020年09月20日 - 18:09

原始链接:http://hexo.gyrojeff.moe/2020/09/20/P1714-%E5%88%87%E8%9B%8B%E7%B3%95-%E5%8D%95%E8%B0%83%E9%98%9F%E5%88%97/

许可协议: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 转载请保留原文链接及作者!

题目

题目描述

今天是小Z的生日,同学们为他带来了一块蛋糕。这块蛋糕是一个长方体,被用不同色彩分成了N个相同的小块,每小块都有对应的幸运值。

小Z作为寿星,自然希望吃到的第一块蛋糕的幸运值总和最大,但小Z最多又只能吃M小块(M≤N)的蛋糕。

吃东西自然就不想思考了,于是小Z把这个任务扔给了学OI的你,请你帮他从这N小块中找出连续的k块蛋糕(k≤M),使得其上的幸运值最大。

输入格式

输入文件cake.in的第一行是两个整数N,M。分别代表共有N小块蛋糕,小Z最多只能吃M小块。

第二行用空格隔开的N个整数,第i个整数Pi代表第i小块蛋糕的幸运值。

输出格式

输出文件cake.out只有一行,一个整数,为小Z能够得到的最大幸运值。

输入输出样例

输入 #1
5 2
1 2 3 4 5
输出 #1
9
输入 #2
6 3
1 -2 3 -4 5 -6
输出 #2
5

说明/提示

对20%的数据,N≤100。

对100%的数据,N≤500000,|Pi|≤500。 答案保证在2^31-1之内。

题解

设$f[i]$为以$i$个元素结尾的连续子段的最大值:

$$ f[i] = \max_{i - m \leq j \leq i - 1} \{\text{sum}[i] - \text{sum}[j]\} $$

改写:

$$ f[i] = \text{sum}[i] - \min_{i - m \leq j \leq i - 1} \text{sum}[j] $$

显而易见,我们要维护一个最小值的单调队列,也就是一个递增的单调队列,这样我们就可以得到线性的复杂度。

不过我的做法有点黑魔法(雾),我想的时候是这么想的:以第$i$个元素开始的连续子段的最大值,这样其实就要维护一个递减的单调队列,不过时间复杂度就变成了$O(m + n) \approx O(n)$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <cstdio>
#define int long long

using namespace std;

const int maxn = 500005 * 2;
struct node { int val, pos; } q[maxn];
int a[maxn], s[maxn], m, n;

signed main() {
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
cin >> a[i]; s[i] = s[i - 1] + a[i];
}
for (int i = n + 1; i <= m + n; i ++) s[i] = s[n];
int head = 0, tail = 0;
int ans = -8e18;
// max --> decrease
for (int i = 1; i <= n + m; i ++) {
while (head < tail && s[i] >= q[tail - 1].val) tail --;
q[tail ++] = (node) { s[i], i };
while (head < tail && i - q[head].pos + 1 > m) head ++;
ans = max(ans, q[head].val - s[(i - m >= 0) ? i - m : 0]);
}
cout << ans << endl;
return 0;
}
posted @ 2020-11-10 17:54  gyro永不抽风  阅读(140)  评论(0编辑  收藏  举报