2020-2021 ICPC Northwestern European Regional Programming Contest (NWERC 2020) G Great Expectations

题目描述

https://codeforces.com/gym/103049/problem/G

简要题意:现在有一个迷宫,迷宫是一个长度为 $n$ 的数轴,共有 $m$ 个 $boss$,第 $i$ 个 $boss$ 的位置为 $t_i$,秒杀成功的概率为 $p_i$,秒杀失败则需要花 $d_i$ 的时间,你的速度为 $1$,现在这个迷宫的通关记录为 $r$,求如果你想破纪录期望最少需要花费多少时间,你可以在任何时候重来

$n < r\le 5000,m\le 50$

Solution

显然我们只会在秒杀某个 $boss$ 失败后才会重来,我们令 $f[i][j]$ 表示已经秒杀第 $i$ 个 $boss$,且走到第 $i+1$ 个 $boss$ 前还没有挑战,期望最少花费多少时间破纪录

显然我们有 $f[n][k]=n-t_m$,其中 $k\in[0,r-m)$,那么对于 $f[i][j]$ 的转移,我们可以得到其值为

1
2
f[i][j] = p[i] * (t[i + 1] - t[i] + f[i + 1][j]) + 
(1 - p[i]) * min(j + d[i] >= k - m ? INF : d[i] + t[i + 1] - t[i] + f[i + 1][j + d[i]], f[0][0]);

我们注意到转移中出现 $f[0][0]$,且有一个 $min$,我们不能直接设 $f[0][0]$ 的值为 $x$,但我们可以二分 $f[0][0]$ 的值,如果最后算出来的 $f[0][0]$ 比我们二分的值大,则说明我们二分值太小,否则太大,按照这个结果二分即可

时间复杂度 $O(nm\log )$

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 <iostream>
#include <iomanip>
#include <algorithm>
#include <vector>
#define maxn 52
#define maxm 5010
#define INF 100000000000000000
#define ll long long
using namespace std;

const long double eps = 1e-9;

int n, m, k, t[maxn], d[maxn];
double p[maxn];

double f[maxn][maxm];
bool check(double x) {
for (int i = 0; i < k - m; ++i) f[n + 1][i] = 0;
for (int i = n; ~i; --i)
for (int j = 0; j < k - m; ++j) {
f[i][j] = p[i] * (t[i + 1] - t[i] + f[i + 1][j]) +
(1 - p[i]) * min(j + d[i] >= k - m ? INF :
d[i] + t[i + 1] - t[i] + f[i + 1][j + d[i]], x);
}
return f[0][0] <= x;
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> m >> k >> n;
for (int i = 1; i <= n; ++i) cin >> t[i] >> p[i] >> d[i];
p[0] = 1; t[n + 1] = m;
double l = 0, r = 1e9, mid, ans;
for (int i = 0; i < 100; ++i) {
mid = (l + r) / 2;
if (check(mid)) ans = mid, r = mid;
else l = mid;
} cout << fixed << setprecision(7) << ans << "\n";
return 0;
}