Dyd's Blog

He who has a strong enough why can bear almost any how.

luoguP2254 [NOI2005] 瑰丽华尔兹

实现有点麻烦

瑰丽华尔兹

思路

不难发现,对于每一段,如果要停,就在最后停,答案不变,考虑设 $d[i][x][y]$ 表示“第 $i$ 段时间,在 $x, y$ 停下的最长路”,转移显然是:
$$
d[i][x][y] = \max(d[i - 1][x’][y’]) + abs(x - x’) + abs(y - y’)
$$
不难发现,对于每个 $i$ ,方向是固定的, $x, y$ 中只会有一个变化,且是单调的,所以直接上单调队列,第一维可以滚动掉,时间为 $O(knm)$

代码

单调队列一定要看清楚队头和队尾!

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
43
44
45
46
47
48
49
50
51
52
#include <bits/stdc++.h>
#define fi first
#define se second
using PII = std::pair<int, int>;
const int N = 200 + 100, INF = 0x3f3f3f3f;
int n, m, num, a, b, d[2][N][N], l, r, ans, dx[5] = {0, -1, 1, 0, 0}, dy[5] = {0, 0, 0, -1, 1};
PII q[N];
char mp[N][N];
struct Time{ int s, t, d; } c[N];
bool in(int x, int y){ return x >= 1 && y >= 1 && x <= n && y <= m; }
bool in(int x, int y, int sx, int sy, int len){ return std::abs(x - sx) + std::abs(y - sy) <= len; }
void work(int sx, int sy, int len, int o, int id)
{
l = 1, r = 0;
for (; in(sx, sy); sx += dx[id], sy += dy[id])
{
if (mp[sx][sy] == 'x')
{
l = 1, r = 0;
continue;
}
while (l <= r && d[o][q[r].fi][q[r].se] + std::abs(sx - q[r].fi) + std::abs(sy - q[r].se) <= d[o][sx][sy]) --r;
while (l <= r && !in(q[l].fi, q[l].se, sx, sy, len)) ++l;
q[++r] = {sx, sy};
if (l <= r) d[o ^ 1][sx][sy] = d[o][q[l].fi][q[l].se] + std::abs(sx - q[l].fi) + std::abs(sy - q[l].se);
}
}
int main()
{
scanf("%d %d %d %d %d", &n, &m, &a, &b, &num);
for (int i = 1; i <= n; ++i) scanf("%s", mp[i] + 1);
for (int i = 1; i <= num; ++i) scanf("%d %d %d", &c[i].s, &c[i].t, &c[i].d);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) d[0][i][j] = -INF;
d[0][a][b] = 0;
for (int i = 1, o = 0, len; i <= num; ++i, o ^= 1)
{
len = c[i].t - c[i].s + 1;
if (c[i].d == 1)
for (int j = 1; j <= m; ++j) work(n, j, len, o, 1);
else if (c[i].d == 2)
for (int j = 1; j <= m; ++j) work(1, j, len, o, 2);
else if (c[i].d == 3)
for (int j = 1; j <= n; ++j) work(j, m, len, o, 3);
else
for (int j = 1; j <= n; ++j) work(j, 1, len, o, 4);
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) ans = std::max(ans, d[num & 1][i][j]);
printf("%d\n", ans);
return 0;
}