0%

「HNOI2015」亚瑟王 题解

题目描述

Link

LOJ 的题面比 Luogu 的好看qwq

题解

首先根据期望的线性性,最后的答案为 \(\sum\limits_{i = 1}^n a_i * f_i\),其中 \(f_i\) 表示 \(i\) 真实发动的概率。

为什么 \(f_i\) 不等于 \(p_i\) 呢,因为题中有限制:用了此张牌后进入下个回合,这时候每张牌的概率就变了。

那么考虑计算 \(f\),令 \(dp_{i, j}\) 表示 \(r\) 轮中在前 \(i\) 张里面取了 \(j\) 张的概率。先把 \(i = 1\) 的算出来 \(dp_{1, 0}\)\(r\) 轮中都没选到 \(1\) 的概率为 \((1 - p_1)^r\)\(dp_{1, 1}\)\(f_1\) 相等为 \(1 - (1 - p_1)^r\)

然后考虑 \(dp_{i, j}\) 由什么得来,分类讨论: - 发动第 \(i\) 张牌,那么概率为 \(dp_{i - 1, j - 1} * (1 - (1 - p_i)^{r - j - 1})\); - 如果不发动,那么概率为 \(dp_{i - 1, j} * (1 - p_i)^{r - j}\)

最终 \(dp_{i, j}\) 为两个加起来。

那么 \(f_i\) 就是 \(\sum\limits_{j = 0}^r dp_{i - 1, j} * (1 - (1 - p_i)^{r - j})\),意为在 \(j\) 轮内发动过。

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
#include <algorithm>
#include <cstdio>
#include <cstring>

const int N = 230;

int d[N];
double p[N], f[N];
double dp[N][N];

double FastPow(double a, int b) {
double res = 1;
for (; b; b >>= 1, a = a * a)
if (b & 1)
res = res * a;
return res;
}

int main() {
int T;
scanf("%d", &T);
while (T--) {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%lf%d", &p[i], &d[i]);
memset(dp, 0, sizeof(dp));
memset(f, 0, sizeof(f));
dp[1][0] = FastPow(1 - p[1], m);
dp[1][1] = f[1] = 1 - FastPow(1 - p[1], m);
for (int i = 2; i <= n; ++i)
for (int j = 0; j <= std::min(i, m); ++j) {
if (j)
dp[i][j] += dp[i - 1][j - 1] * (1 - FastPow(1 - p[i], m - j + 1));
if (i != j)
dp[i][j] += dp[i - 1][j] * FastPow(1 - p[i], m - j);
}
for (int i = 2; i <= n; ++i)
for (int j = 0; j <= std::min(i - 1, m); ++j)
f[i] += dp[i - 1][j] * (1 - FastPow(1 - p[i], m - j));
double ans = 0;
for (int i = 1; i <= n; ++i)
ans += d[i] * f[i];
printf("%.10lf\n", ans);
}
return 0;
}