题意:两个瓶子里都装了n个糖果;从第一个瓶子拿的概率是p , 当你再拿糖果的时候,发现瓶子空了 ,求这时候另外一个瓶子的剩余的糖果的数量的期望 (1 ≤ n ≤ 2 × 10^5)
这题的概率DP公式好求,即
但c(n,m) 太大,会上溢出, p^k 太小,会下溢出,这时就要用到 log 求组合了
对于问题一:
排列过大,考虑到 y = logx 函数, log(n!) 也不会很大. 又
令 f(x) = log( x! ),则有
预处理出 函数f() 就可以 O(1)求出 log( \binom{n}{m} )了. 然后 再求个 exp( x ) 就可以还原了.
exp(x ) 返回以 e 为底数、以给定数字为指数的幂。
对于问题二:
p^x 值过小. log( p^x ) = x * log p 这样值就在可控范围内. 同样一次 exp( x ) 就可以还原
代码如下:
#include<iostream> #include<cstdio> #include<string.h> #include<math.h> #include<vector> #include<algorithm> using namespace std; #define N 400005 double f[N]; double C(int n,int m) { return f[n]-f[m]-f[n-m]; //c(n,m)=n!/((n-m)!*m!) log(c(n,m))=log(n!)-log(m!)-log((m-n)!) } int main() { int i,n,j,t=0; f[0]=0; for(i=1;i<N;i++) f[i]=f[i-1]+log(i*1.0); double p,q,res; while(~scanf("%d%lf",&n,&p)) { t++; q=1.0-p; res=0; for(i=0;i<n;i++) { res=res+1.0*(n-i)*(exp( C(n+i,i)+i*log(p)+(n+1)*log(q))+exp(C(n+i,i)+i*log(q)+(n+1)*log(p))); } printf("Case %d: %.6lf\n",t,res); } return 0; }
还有一种方法是直接求的,没用log
是边求组合数一边求概率
#include<iostream> #include<cstdio> #include<string.h> #include<math.h> #include<vector> #include<algorithm> using namespace std; int main() { int i,n,j,t=0; double p,q,res,ca,cb; while(~scanf("%d%lf",&n,&p)) { t++; q=1.0-p; ca=cb=1.0; int exp=n+1; res=n*(pow(q,exp)+pow(p,exp)); for(i=1; i<n; i++) { ca=ca*(n+i)*p/i; cb=cb*(n+i)*q/i; while(ca>n||cb>n) { ca=ca*q; cb=cb*p; exp--; } res+=ca*(n-i)*pow(q,exp); res+=cb*(n-i)*pow(p,exp); } printf("Case %d: %.6lf\n",t,res); } return 0; }