[HNOI 2015]亚瑟王
Description
小 K 不慎被 LL 邪教洗脑了,洗脑程度深到他甚至想要从亚瑟王邪教中脱坑。
Input
输入文件的第一行包含一个整数 T,代表测试数据组数。
Output
对于每组数据,输出一行,包含一个实数,为这套卡牌在这一局游戏中造成的
Sample Input
3 2
0.5000 2
0.3000 3
0.9000 1
Sample Output
HINT
一共有 13 种可能的情况:
题解
利用期望的线性性 $E(x+y) = E(x)+E(y)$ 可知,这题我们可以先求出每张牌的打出概率 $fp_i$ ,然后就可以得出 $$ans = \sum_{i = 1}^n fp_i*d_i$$
这道题需要用到的一个公式:
在不考虑其他牌的前提下,若一张牌一轮打出的概率为 $p$ ,则在 $r$ 轮中打出这张牌的概率为: $$1-(1-p)^r$$
简要证明:
记要求的总概率为 $P$ ,显然
\begin{aligned}
P &= p+p*(1-p)+p*(1-p)^2+…+p*(1-p)^{r-1} \\
& = \frac{p*(1-(1-p)^r)}{1-(1-p)}\\
& = 1-(1-p)^r
\end{aligned}
另外我们发现,单独考虑每张牌的概率的时候,影响其的只有他前面选了几张。
我们不妨记一个辅助数组 $f_{i, j}$ 为总 $r$ 轮后前 $i$ 张牌中选中了 $j$ 张牌的概率。
容易发现: $$fp_i = \sum_{j = 0}^n f_{i-1, j}*(1-(1-p_i)^{r-j})$$
现在我们就是考虑 $f_{i, j}$ 如何转移。
第一种, $f_{i, j}$ 从 $f_{i-1, j}$ 转移过来,即第 $i$ 张牌最终没有选,始终不选第 $i$ 张牌的概率是 $(1-p_i)^{r-j}$
$$f_{i, j} += f_{i-1, j}*(1-p_i)^{r-j}(i>0)$$
第二种,当 $j>0$ 时, $f_{i, j}$ 可以从 $f_{i-1, j-1}$ 转移过来,表示最终选择了第 $i$ 张牌
这时候,有 $j-1$ 轮没有考虑到第 $i$ 张牌,所以考虑到第 $i$ 张牌的轮数是 $r-j+1$ ,最终选择的概率为 $1-(1-p_i)^{r-j+1}$
$$ f_{i, j} += f_{i-1, j-1}*(1-(1-p_i)^{r-j+1})(i>0,j>0)$$
总时间复杂度 $O(Tnr)$ 。
1 //It is made by Awson on 2018.1.2 2 #include <set> 3 #include <map> 4 #include <cmath> 5 #include <ctime> 6 #include <queue> 7 #include <stack> 8 #include <cstdio> 9 #include <string> 10 #include <vector> 11 #include <cstdlib> 12 #include <cstring> 13 #include <iostream> 14 #include <algorithm> 15 #define LL long long 16 #define LD long double 17 #define Max(a, b) ((a) > (b) ? (a) : (b)) 18 #define Min(a, b) ((a) < (b) ? (a) : (b)) 19 using namespace std; 20 const int N = 220; 21 22 int n, r; 23 LD p[N+5], d[N+5]; 24 LD pre[N+5][N+5], f[N+5][N+5], fp[N+5]; 25 26 void work() { 27 scanf("%d%d", &n, &r); 28 for (int i = 1; i <= n; i++) scanf("%Lf%Lf", &p[i], &d[i]), pre[i][0] = 1; 29 for (int i = 1; i <= n; i++) for (int j = 1; j <= r; j++) pre[i][j] = pre[i][j-1]*(1-p[i]); 30 memset(f, 0, sizeof(f)); memset(fp, 0, sizeof(fp)); 31 f[0][0] = 1; 32 for (int i = 1; i <= n; i++) 33 for (int j = 0; j <= r; j++) { 34 fp[i] += f[i-1][j]*(1-pre[i][r-j]); 35 f[i][j] += f[i-1][j]*pre[i][r-j]; 36 if (j > 0) f[i][j] += f[i-1][j-1]*(1-pre[i][r-j+1]); 37 } 38 LD ans = 0; 39 for (int i = 1; i <= n; i++) ans += d[i]*fp[i]; 40 printf("%Lf\n", ans); 41 } 42 int main() { 43 int t; cin >> t; 44 while (t--) work(); 45 return 0; 46 }