[CF2045D] Aquatic Dragon
方便起见,下文用水陆空三栖 车 代替 dragon,用 油量 代替 stamina。油箱容量没有上限。
记一次 walk 操作花费的时间为 \(a\),一次 swim 花费 \(b\),一次 fly 花费 \(c\),\(s_i=\sum_{j=1}^i p_i\)。
显然,人不会到车左边,车不会后退。
假设当前人和车都在第 \(i\) 个点,油箱有 \(j\) 升油。
- fly 到 \(i+1\),油量变为 \(p_{i+1}\),代价为 \(c\)。
- \(j \ge d\),直接 swim,油量 \(j-d+p_{i+1}\),代价为 \(b\)。
- \(j < d\),假设人 walk 到 \(t\) 之后返回 \(i\),显然在 \([i, t - 1]\) 之内车只能 swim。
- 若 \(j + s_t - s_i - d(t - i) \ge 0\),那么在 \(t-1\rightarrow t\) 时可以 swim。不等号左边为新的油量,代价为 \((2a+b)(t-i)\)。
- 若 \(j + s_t - s_i - d(t - i - 1) > 0\),那么在 \(t-1\rightarrow t\) 时可以 fly。到达 \(t\) 后油量归 \(0\),且代价为 \((2a+b)(t-i)-b+c\)。
考虑优化上述暴力。
如果认为经过一次 fly 之后得到的 \((i, 0)\) 或 \((i, p_i)\) 是特殊的,那么只有 \(O(n)\) 个特殊状态,其他状态一定通过某个特殊状态让车一直 swim 得到。设 \(t_j \in \{0, p_j\}\),那么从 \((j, t_j)\) 车只 swim 只能转移到一些合法的 \((i, s_i - s_j + t_j)\)。
从前往后 DP,尝试使用数据结构维护每个特殊状态 swim 到 \(i\) 的代价。并在外部记录偏移量:默认所有的 \(i-1 \rightarrow i\) 过程都经过水路,且都经过了往返 walk 拾取的过程。
对于 \(i\) 处新产生的特殊状态,计算初始代价,转移条件:
- 当前 \((i, 0)\) 来自之前的 \((j, t_j)\),需要 \((s_i - s_j + t_j) - d(i - j - 1) > 0\);
- 当前 \((i, p_i)\) 来自之前的 \((j, t_j)\),需要 \((s_{i-1} - s_j + t_j) - d(i - j - 1) > 0\)。
对于已有的所有特殊状态,如果 \((s_i - s_j + t_j) - d(i - j)\ge 0\),这意味着 \((j, t_j)\) 可在 \(i - 1 \rightarrow i\) 处不需要经过往返 walk 的拾取,不需要加上额外预定的 \(2a\)。
观察发现,三个不等式都可以变成 \(s_j - t_j - d_j\) 小于等于某个数的形式,于是按此排序离散化后,作为线段树的下标即可。
最终答案考虑最后一步是 swim 还是 fly。
一些细节参考实现。
int n, m;
LL a, b, c, d, p[MAXN], sp[MAXN], val[MAXN], dsc[MAXN<<1];
int main() {
read(n, d, b, c, a);
for (int i = 1; i <= n; ++i) read(p[i]), sp[i] = sp[i-1] + p[i];
for (int i = 1; i <= n; ++i)
dsc[++m] = sp[i-1] - i * d, dsc[++m] = sp[i] - i * d;
sort(dsc+1, dsc+m+1), m = unique(dsc+1, dsc+m+1)-dsc-1;
auto findle = [&](LL v) -> int { return upper_bound(dsc+1, dsc+m+1, v)-dsc-1; };
SGT::build(1, 1, m), SGT::mdf(1, 1, m, findle(-d), 0);
LL ans = INF;
for (int i = 2; i <= n; ++i) {
LL u = SGT::qry(1, 1, m, 1, findle(sp[i]-d*(i-1)-1)) + c - b;
LL v = SGT::qry(1, 1, m, 1, findle(sp[i-1]-d*(i-1)-1)) + c - b;
SGT::mdf(1, 1, m, findle(sp[i]-i*d), u);
SGT::mdf(1, 1, m, findle(sp[i-1]-i*d), v);
SGT::upd(1, 1, m, 1, findle(sp[i-1]-d*i), -2 * a);
}
ans = min(ans, SGT::qry(1, 1, m, 1, findle(sp[n]-d*n)));
write(ans + (n - 1) * (a * 2 + b));
return 0;
}
一开始讨论漏了向前拾取一段到 \(t\) 之后,在 \(t-1 \rightarrow t\) 使用 fly 的情况,在极不清醒的情况下调了一上午。
4 10 10 100 1
5 1 5 1
ans = 216

浙公网安备 33010602011771号