gyro永不抽风

ああああああああああああああああおおおおおおおおおおおおおおおお

P1714 切蛋糕 - 单调队列

题目

题目描述

今天是小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$个元素结尾的连续子段的最大值:

改写:

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

不过我的做法有点黑魔法(雾),我想的时候是这么想的:以第$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;
}
__EOF__
-------------本文结束感谢您的阅读-------------

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

文章作者:gyro永不抽风

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

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

原始链接:http://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) 转载请保留原文链接及作者!

真的不买杯奶茶吗?T^T

欢迎关注我的其它发布渠道