Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

CF372C Watching Fireworks is Fun

LuoGu: CF372C Watching Fireworks is Fun

CF: C. Watching Fireworks is Fun

先考虑暴力DP,$f _ {i, j}$ 表示当前放第 $i$ 个烟花在 $j$ 位置要获得的最大开心值。那么 $f _ {i, j} = (\max _ {k = j - \Delta t d} ^ {j + \Delta td} f_{i - 1, k}) + b _ i - \left | a _ i - j \right |$ ,对于每个 $i$ 和 $j$ 枚举所有可行 $k$ ,可以将 $f _ {i, j}$ 算出来。时间复杂度 $O(n ^ 2 m)$ ,显然是不能通过的。

考虑如何优化。不难发现,当 $i$ 固定了,那么 $\Delta td$ 也是固定的,那么就是取一段 $2\Delta td$ 长度的滑动窗口,所以可以考虑使用单调队列。对于每个 $j$ 先将所有的 $j + \Delta td$ 的元素补齐,将多余的元素删除,即可维护当前 $j$ 的队列。注意判断边界,位置不能大于 $n$ 不能小于 $1$ 。最终的答案就是 $\max_{i = 1} ^ n f _ {m, i}$ 。再考虑空间问题,空间复杂度为 $O(nm)$ ,实际上可以将 $m$ 一维滚动掉。

查看代码
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 15e4 + 10, M = 310;
const LL inf = 1e18;
LL A[M], B[M], t[M], f[2][N];
int n, m, d;
int hd, tl, q[N];
int main()
{
scanf("%d%d%d", &n, &m, &d);
for (int i = 1; i <= m; i++)
scanf("%lld%lld%lld", &A[i], &B[i], &t[i]);
for (int i = 1; i <= n; i++)
f[0][i] = 0;
for (int i = 1; i <= n; i++)
f[1][i] = -inf;
for (int i = 1, k; i <= m; i++)
{
k = 1;
hd = 1, tl = 0;
LL dis = d * (t[i] - t[i - 1]);
for (int j = 1; j <= n; j++)
{
for (; k <= min((LL)n, j + dis); k++)
{
while (hd <= tl && f[i - 1 & 1][q[tl]] <= f[i - 1 & 1][k])
tl--;
q[++tl] = k;
}
while (hd <= tl && q[hd] < max(1ll, j - dis))
hd++;
f[i & 1][j] = f[i - 1 & 1][q[hd]] - abs(A[i] - j) + B[i];
}
}
LL mx = -inf;
for (int i = 1; i <= n; i++)
mx = max(mx, f[m & 1][i]);
printf("%lld", mx);
return 0;
}