UVa 1639 - Candy(数学期望 + 精度处理)

链接:

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4514

 

题意:

有两个盒子各有n(1≤n≤2e5)个糖,每天随机选一个(概率分别为p,1-p),然后吃一颗糖。
直到有一天,打开盒子一看,没糖了!输入n,p,求此时另一个盒子里糖的个数的数学期望。

 

分析:

根据期望的定义,不妨设最后打开第1个盒子,此时第2个盒子有i颗,则这之前打开过n+(n-i)次盒子,
其中有n次取的是盒子1,其余n-i次取的盒子2,概率为C(2n-i,n)(p^(n+1))((1-p)^(n-i))。
注意p的指数是n+1,因为除了前面打开过n次盒子1之外,最后又打开了一次。
这个概率表达式在数学上是正确的,但是用计算机计算时需要小心:
n可能高达20万,因此C(2n-i,n)可能非常大,而(p^(n+1))和((1-p)^(n-i))却非常接近0。
如果分别计算这3项再乘起来,会损失很多精度。
一种处理方式是利用对数,设v1(i) = ln(C(2n-i,n)) + (n+1)ln(p) + (n-i)ln(1-p),
则“最后打开第1个盒子”对应的数学期望为e^v1(i)。
同理,当最后打开的是第2个盒子,对数为v2(i) = ln(C(2n-i,n)) + (n+1)ln(1-p) + (n-i)ln(p),概率为e^v2(i)。
根据数学期望的定义,最终答案为sum{i(e^v1(i)+e^v2(i))}。

 

代码:

 1 #include <cstdio>
 2 #include <cmath>
 3 
 4 const int UP = 2e5 * 2 + 5;
 5 long double logF[UP];
 6 
 7 // C(n,m) = n!/(m!(n-m)!)
 8 long double logC(int n, int m) {
 9     return logF[n] - logF[m] - logF[n-m];
10 }
11 
12 double solve(int n, double p) {
13     double ans = 0;
14     for(int i = 0; i <= n; i++) {
15         long double c = logC(n+n-i, n);
16         long double v1 = c + (n+1)*log(p) + (n-i)*log(1-p);
17         long double v2 = c + (n+1)*log(1-p) + (n-i)*log(p);
18         ans += i * (exp(v1) + exp(v2));
19     }
20     return ans;
21 }
22 
23 int main() {
24     logF[0] = 0;
25     for(int i = 1; i < UP; i++) logF[i] = logF[i-1] + log(i);
26     int n;
27     double p;
28     for(int cases = 1; ~scanf("%d%lf", &n, &p); cases++) {
29         printf("Case %d: %.6f\n", cases, solve(n, p));
30     }
31     return 0;
32 }

 

posted @ 2018-08-30 14:23  Ctfes  阅读(181)  评论(0编辑  收藏  举报