生成函数(EGF)练习, UVA1305 Chocolate 题解

洛谷地址,但不建议在UVA上测,建议去POJ

题意描述

有一袋无限多个的球,c种颜色,每次拿出一个放在桌上,若拿出的球与桌上的一个球颜色相同,就把两个球都丢进垃圾桶
拿n次,求最后桌上剩下m个球的概率

题解

仔细考虑,会发现m不能大于c、n的任何一个,所以m小于100
可以想到,若一个颜色的球被拿出过奇数次,这个颜色就会对答案做出贡献,所以我们需要m种颜色被拿出m次,剩下的拿出偶数次
每个颜色独立等概率,所以分开计算
设一个颜色球被拿出奇数次的概率的生成函数为\(G(x)=\sum{\frac{1}{i!}*x^i}\)(i为奇数)
同样的\(F(x)=\sum{\frac{1}{i!}*x_i}\)(i为偶数)
由于这题求的数是组合数(每个球都一样,所以不是排列),所以我们的生成函数是\(EGF\)(也就是要除以i!,想深入了解的可自行baidufirstsearch)
然后我们想求的就是

\[ans=\frac{C^{m}_{c}}{c^n}*G(x)^m*F(x)^{c-m}*n! \]

然后为什么要乘上\(n!\)呢,我们考虑一个简化的小问题
将3种颜色的球(各有a,b,c个)放成一排,答案是\(C^{a}_{a+b+c}*C^{b}_{a+c}*C^{c}_{c}\)
化简得\(\frac{(a+b+c)!}{a!*b!*c!}\)
这里也就是这个道理了
然后根据一个公式:\(e^x=\sum{\frac{x_i}{i!}}\)(啊我不会证,建议按\(LH\)大佬说的,背下来)
\(F(x)=\frac{e^x+e^{-x}}{2}\)(关于为什么是\(e^x+e^{-x}\),请将\(-x\)带入原多项式试试看)
同理\(G(x)=\frac{e^x-e^{-x}}{2}\)
那么我们就要求

\[(\frac{e^x-e^{-x}}{2})^m*(\frac{e^x+e^{-x}}{2})^{c-m} \]

把分母提取出来,用快速幂直接算,分子由二项式定理展开

\[\sum{(-1)^i*C^{i}_{m}*e^{i}*e^{-(m-i)}}*\sum{C^{j}_{c-m}*e^{j}*e^{c-m-j}} \]

化简

\[\sum{\sum{(-1)^i*C^{i}_{m}*C^{j}_{c-m}*e^{c-2*i-2*j}}} \]

我们用上面那个我不会证的柿子再推回去

\[\sum{\sum{(-1)^i*C^{i}_{m}*C^{j}_{c-m}*\sum{\frac{(c-2*i-2*j)^k}{k!}*x^k}}} \]

考虑生成函数的性质(或者是定义?),生成函数的系数就是问题的答案,所以我们需要的只有第n项
也就是把k固定为n(如果看到这里懵逼了,建议休息一下,突然看到代入相信会把不少人吓一跳)
所以柿子就变成了

\[\sum{\sum{(-1)^i*C^{i}_{m}*C^{j}_{c-m}*\frac{(c-2*i-2*j)^n}{n!}*x^n}} \]

乘上刚刚说的n!,就把n!消掉了
最终柿子为

\[ans=\frac{C^{m}_{c}}{c^n*2^c}*\sum{\sum{(-1)^i*C^{i}_{m}*C^{j}_{c-m}*\frac{(c-2*i-2*j)^n}{n!}}} \]

由于c、m都比较小,所以时间复杂度很优秀
然后上代码,由于UVA恶臭,没有在洛谷上AC,只在poj上过了,代码也因为瞎改搞得比较丑,参考就好

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll double
#define int long long
using namespace std;
int n,m,c;
ll ans,C[105][105];

static inline ll ksm(ll x,int k){
    ll tmp=1,X=x;
    while(k){
        if((k%2)==1)tmp=tmp*X;
        X=X*X,k/=2;
    }
    return tmp;
}

signed main(){
    C[0][0]=1.0;
    for(int i=1;i<=103;i++){
        C[i][0]=1.0;
        for(int j=1;j<=i;j++)C[i][j]=C[i-1][j-1]+C[i-1][j];
    }
    while(cin>>c){
        if(!c)break;
        scanf("%lld%lld",&n,&m),ans=0.0;
        if(m>c||m>n||(n-m)%2==1){
            printf("0.000\n");
            continue;
        }
        for(int i=0;i<=m;i++){
            for(int j=0;j<=c-m;j++){
                ans+=((i&1)==1?-1:1)*C[m][i]*C[c-m][j]*ksm((c-2*i-2*j)*1.0/c,n);
            }
        }
        ans=ans*C[c][m];
        ans/=ksm(2.0,c);
        printf("%.3lf\n",ans);
    }
}
posted @ 2021-01-29 14:41  蒟蒻丁  阅读(48)  评论(0编辑  收藏  举报