生成函数(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)
然后我们想求的就是
然后为什么要乘上\(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}\)
那么我们就要求
把分母提取出来,用快速幂直接算,分子由二项式定理展开
化简
我们用上面那个我不会证的柿子再推回去
考虑生成函数的性质(或者是定义?),生成函数的系数就是问题的答案,所以我们需要的只有第n项
也就是把k固定为n(如果看到这里懵逼了,建议休息一下,突然看到代入相信会把不少人吓一跳)
所以柿子就变成了
乘上刚刚说的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);
}
}