题目大意 :两个盒子开始都有n块糖,每次从一个中拿糖的概率为p,求当一个盒子空时,另一个盒子中糖的数目期望
很明显,200000太大了,不能直接排列组合。
然后需要一个快速排列组合函数:logC(m,n)
<span style="font-size:14px;">f[0]=0;
for(int i=1;i<=400002;i++) f[i]=f[i-1]+log(i*1.0);
double logC(int m,int n){
return f[m]-f[n]-f[m-n];
}</span>
有了上面的算式之后,C(m,n)=exp(logC(m,n))
问题解决了吧。
但是其实问题没有解决,我们在比赛的时候想了一个小时,怎么处理double的上溢出和下溢出。。。
最后被函数中的log给提醒了。直接全部取log不就好了!
数值计算方法没学好啊
![hdu 4465 Candy 快速全排列 2012 Asia Chengdu Regional Contest B - Mike - Apple](https://i-blog.csdnimg.cn/blog_migrate/260c29dbc47f7b1cd0b3177c3c2e5922.gif)
所以算式变为
![hdu 4465 Candy 快速全排列 2012 Asia Chengdu Regional Contest B - Mike - Apple](http://img7.ph.126.net/VkiIV2qVH5bqbcBrRiiEoA==/6597731672656468047.jpg)
<span style="font-size:14px;">#include<cstdio>
#include<cmath>
double f[400005];
double logC(int m,int n){
return f[m]-f[n]-f[m-n];
}
int main(){
f[0]=0;
for(int i=1;i<=400002;i++) f[i]=f[i-1]+log(i*1.0);
int apple=1,n;
double p,q;
while(~scanf("%d",&n)){
scanf("%lf",&p);
q=1-p;
double ans=0;
for(int k=0;k<=n;k++){
ans+=(n-k)*(exp(logC(n+k,k)+(n+1)*log(q*1.0)+k*log(p*1.0))+exp(logC(n+k,k)+(n+1)*log(p*1.0)+k*log(q*1.0)));
}
printf("Case %d: %.6f\n",apple++,ans);
}
return 0;
}</span>
法二:
题目分析 :不难得到公式: ans = 求和(i = 0~n)(n-i) * C(i, n + i) * ( (p ^ n* (1 - p) ^ i) + (p ^ i + (1 - p) ^ n)),
本题n上限为20000,组合数存不下故中间过程要用递推公式即an = an * n / (n + 1) *(n + i) / i * p这样还是会卡精度有点坑爹,可以采用一个很简单的技巧。见程序
<span style="font-size:14px;">#include <cstdio>
#include <cmath>
int main()
{
int n, ca = 0;
double p;
while(scanf("%d %lf", &n, &p) != EOF)
{
double tmp1 = n, tmp2 = n ;
// printf("tmp1 = %f tmp2 = %f\n", tmp1, tmp2);
double ans1 = tmp1 * pow(1 - p, n + 1);
double ans2 = tmp2 * pow(p, n + 1);
int now = n;
int re = n + 1;
for(int i = 1; i < n; i++)
{
now = n - i; //系数
//递推公式
tmp1 *= 1.0 / (double)(now + 1) * p / i * (n + i) * now;
tmp2 *= 1.0 / (double)(now + 1) * (1 - p) / i * (n + i) * now;
//当tmp大于某个设定的值时让它乘p(或(1-p))来保证其数字不会暴精度
//关键技巧
while(tmp1 > n || tmp2 > n)
{
tmp1 *= (1 - p);
tmp2 *= p;
re--;
}
//因为每一项都要乘pow(1-p, n+1)或pow(p,n+1)
//我们在此补上剩余未乘的p(或(1-p))的幂数即可
ans1 += tmp1 * pow(1 - p, re);
ans2 += tmp2 * pow(p, re);
// printf("tmp1 = %lf tmp2 = %lf \n", tmp1, tmp2);
}
printf("Case %d: %lf\n", ++ca, ans1 + ans2);
}
}
</span>