只要你跑的够快,锅就追不上你

0%

「Codeforces 464D」WoD - 2(概率论 + 动态规划)

题目大意

「Codeforces 464D」World of Darkraft - 2

有 $k$ 种装备,初始等级为 $1$(等级为整数)。共有 $n$ 只怪兽,每杀掉一只后会随机掉落一种装备。假设玩家的那件装备的等级为 $l$,则掉落的新装备的等级会在 $[1, l + 1]$ 中均匀随机。拿到装备后,玩家会保留更好的装备,并把相对差的装备卖掉,获得装备等级的收益。问期望收益。

数据范围:$n \le 10^5, k \le 200$,答案与标准答案绝对或相对误差不超过 $10^{-9}$ 即可。

思路分析

首先发现 $k$ 个装备是独立的,我们只需考虑一件装备即可。

令 $dp(i, j)$ 表示打完第 $i$ 只怪,装备等级为 $j$ 的概率。

考虑从 $dp(i)$ 转移到 $dp(i + 1)$。有两种情况:第一种是恰好随机到该装备,并且恰好随机到更高的等级。这样等级会增加一,概率为 $\frac{1}{k \times (j + 1)}$。第二种是其他情况,等级不会发生改变。

在更新 DP 的过程中,我们计算对答案的贡献。如果当前等级为 $j$,则打完一个怪后期望得到:

的收益。直接计算即可。

现在我们已经得到了 $O(n ^ 2)$ 的算法,但是它还不够优秀。我们考虑从题目入手,发现答案与标准答案绝对或相对误差不超过 $10^{-9}$ 即可通过本题。又发现 $dp(i, j)$ 在 $j$ 很大的时候几乎为 $0$。

于是我们设定阈值 $m = 800$,只计算 $j \le m$ 的 DP 值即可。时间复杂度 $O(nm)$。

听说 $m = O(\sqrt{n})$?看来我还是太菜了。

代码实现

1
#include <cstdio>
2
#include <algorithm>
3
using namespace std;
4
5
typedef long double db;
6
const int maxn = 1e5, maxm = 800;
7
int n, m, k;
8
db inv[maxm + 3], dp[2][maxm + 3], ans;
9
10
int main() {
11
	scanf("%d %d", &n, &k);
12
	m = min(n + 1, maxm);
13
	for (int i = 1; i <= maxm + 1; i++) {
14
		inv[i] = (db) 1 / i;
15
	}
16
	int cur = 0, nxt = 1;
17
	dp[cur][1] = 1;
18
	for (int i = 0; i < n; i++) {
19
		for (int j = 1; j <= m; j++) {
20
			dp[nxt][j] = 0;
21
		}
22
		for (int j = 1; j <= m; j++) {
23
			db x = inv[k] * inv[j + 1];
24
			dp[nxt][j] += dp[cur][j] * (1 - x);
25
			dp[nxt][j + 1] += dp[cur][j] * x;
26
			ans += dp[cur][j] * ((j + 1) * (j + 2) / 2 - 1) * inv[j + 1];
27
		}
28
		swap(cur, nxt);
29
	}
30
	double res = ans;
31
	printf("%.18lf\n", res);
32
	return 0;
33
}