题目:
一个爱吃糖的小朋友获得了两盒糖果,每个盒子里均有n(n≤200000)个糖果,他每天随机选一个盒子(概率为 p 和 1-p)吃掉里面的一个,直到有一天,打开盒子一看,没糖了!输入n,p求另一个盒子里剩余糖个数的数学期望。
VJ 题目链接
读完题我拿起计算器 (2e5/365=500+…)???
思路:
起初尝试解个方程,列出来带有组合数无法分离变量,只好放弃转向紫书
其思路大致为:按照另一个盒子剩余数量划分,总共n种情况,表达出第i种情况的概率,所求为 ∑(i<=n)
设最后打开的是1号盒,2号盒剩 i 个,则共打开n+1次1号盒,n-i次2号盒, P1 = C(2n-i, n) * p(n+1) * (1-p)(n-i) ;若最后打开的为2号盒交换 p 和 1-p 即可。但是直接计算组合数会溢出,所以要先取对数再取逆运算
需要注意的是,本题对精度要求很高,我是用的 long double ;非常有意思的是,如果声明p数据类型也为 long double 那么隐式转换的消耗会拖后程序40ms(可能有误差) 所以p仍保留double类型
具体实现:
(260ms 水码)
//#include "stdafx.h"
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <iomanip>
using namespace std;
typedef long double LD;
const int maxn = 4e5 + 1;
LD ln[maxn];
inline LD C(int m, int n) {
return ln[m] - ln[n] - ln[m-n];
}
int main() {
for (int i = 1; i < maxn; i++)//预处理组合数
ln[i] = ln[i - 1] + log(i);
int n, cp = 0;
double p;
while (cin >> n >> p) {
LD E = 0.0;
for (int i = 1; i <= n; i++) {
LD c = C(2*n-i, n);
LD k1 = log(p), k2 = log(1-p);
LD v1 = c + (n + 1)*k1 + (n - i)*k2;//盒1空
LD v2 = c + (n + 1)*k2 + (n - i)*k1;//盒2空
E += i * (exp(v1) + exp(v2));
}
printf("Case %d: %.6Lf\n", ++cp, E);
}
return 0;
}