UVA 1639 - Candy(概率计算+对数处理+精度)

题目链接 https://cn.vjudge.net/problem/UVA-1639

【题意】
两个盒子各有n个糖(n<=2e5)每天选一个盒子,选择第一个盒子的概率为p,选择第二个盒子的概率为1-p,然后拿走盒子里的一颗糖. 直到有一天,打开盒子发现没糖了.输入n和p(0<=p<=1)求出当打开一个盒子发现没有糖时另一个盒子里糖果的数量的数学期望

【思路】
如果最后打开的是第一个盒子,此时第二个盒子里还有 i 颗糖,那么在此之前已经打开了 n+(ni) 个盒子,其中有 n 次选中了第一个盒子,有 ni 次选中了第二个盒子,同时最后一次选中了第一个盒子,所以对应的概率为

P1i=C2ninpn+1(1p)ni

同理如果最后打开的是第二个盒子那么如果第一个盒子中还剩下 i 颗糖的概率为
P2i=C2nin(1p)n+1pni

但要注意的是上面的式子没法直接计算,因为 n105 的规模,组合数会很大,这时就需要用对数来处理后再计算了

V1i=lnP1i=lnC2nin+(n+1)lnp+(ni)ln(1p)
V2i=lnP2i=lnC2nin+(n+1)ln(1p)+(ni)lnp
组合数直接用定义展开即可,预处理一下 lnx 的前缀和就可以快速计算了
最后的期望其实就是
Ans=i=0n(eV1i+eV2i)×i

还有个坑就是这道题用double精度都不够,必须用long double计算才能A

#include<bits/stdc++.h>
using namespace std;

const long double eps=1e-9;
const int maxn=200005;

int n;
double p,q;
long double Ln[maxn];

int main(){
    for(int i=2;i<maxn;++i){
        Ln[i]=Ln[i-1]+log(i);
    }
    int kase=0;
    while(scanf("%d%lf",&n,&p)==2){
        q=1.0-p;
        if(fabs(p)<eps || fabs(q)<eps){
            printf("Case %d: %d\n",++kase,n);
            continue;
        }
        long double ans=0;
        for(int k=0;k<=n;++k){
            long double x=Ln[n+k]-Ln[n]-Ln[k]+(long double)(n+1)*log(p)+(long double)k*log(q);
            ans+=(long double)exp(x)*(n-k);
            x=Ln[n+k]-Ln[n]-Ln[k]+(long double)(n+1)*log(q)+(long double)k*log(p);
            ans+=(long double)exp(x)*(n-k);
        }
        printf("Case %d: %Lf\n",++kase,ans);
    }
    return 0;
}
posted @ 2018-08-24 00:02  不想吃WA的咸鱼  阅读(182)  评论(0编辑  收藏  举报