P1725 琪露诺 题解
思路
动态规划,单调队列
动态规划
观察题目,发现下标为 \(i\) 的点只能对 \([i + l, i + r]\) 区间的点产生贡献。
设 \(f_i\) 为到达点 \(i\) 时的最大冻结指数。
易得状态转移方程式:\(f_k \leftarrow \max(f_k, f_i+a_k),(k \in [i + l, i + r])\)。
若直接对该方程进行转移,时间复杂度是 \(O(n^2)\) 的,会爆。虽然数据水放过去了。
单调队列
考虑优化,对题目给出的条件进行转化,发现能对 \(i\) 产生贡献的,只有 \([i - r, i - l]\) 区间的点。
又因为 \(f_i\) 为到达点 \(i\) 时的最大冻结指数,所以产生贡献的点是明确的,即 \([i - r, i - l]\) 区间中最大值,这恰好又是一个滑动窗口,自然能用单调队列优化。
细节
-
在一些数据中,有一些点是无法到达的,比如这个:
5 3 4 0 1 2 3 4 5
下标为 \(5\) 的点是无法到达的,而如果维护单调队列时 \(f_1\) 为 \(0\) 的话,\(f_5\) 会为 \(5\)。
所以应该把 \(f\) 数组初始化为负无穷的。 -
最后统计答案时只要统计 \([n + 1 - r, n]\) 的区间内的即可。
-
统计答案时要预先把答案制成负无穷。
代码
点击查看代码
/*
--------------------------------
| code by FRZ_29 |
| code time |
| 2024/08/10 |
| 08:16:15 |
| 星期六 |
--------------------------------
*/
#include <iostream>
#include <cstdio>
#include <deque>
using namespace std;
void RD() {}
template<typename T, typename... U> void RD(T &x, U&... arg) {
x = 0; int f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
x *= f; RD(arg...);
}
const int N = 2e5 + 5, inf = -2e9;
#define PB push_back
#define PPB pop_back
#define PPF pop_front
#define LF(i, __l, __r) for (int i = __l; i <= __r; i++)
#define RF(i, __r, __l) for (int i = __r; i >= __l; i--)
int n, l, r, ans = inf;
int f[N], a[N];
deque<int> deq;
int main() {
RD(n, l, r);
LF(i, 0, n) RD(a[i]);
deq.PB(0);
LF(i, 1, n) f[i] = inf;
LF(i, l, n) {
while (deq.size() && i - r > deq.front()) deq.PPF();
// printf("deq.front = %d\n", deq.front());
f[i] = f[deq.front()] + a[i];
while (deq.size() && f[i - l + 1] > f[deq.back()]) deq.PPB();
deq.PB(i - l + 1);
// printf("f[%d] = %d\n", i, f[i]);
}
// LF(i, 0, n) printf("f[%d] = %d ", i, f[i]);
// puts("");
LF(i, n + 1 - r, n) ans = max(ans, f[i]);
printf("%d\n", ans);
return 0;
}
/* ps:FRZ弱爆了,10几天有思路今天才写 */
当你的情人已改名玛丽,你怎能送她一首菩萨蛮?