题目大意:一个口袋中装有巧克力,巧克力的颜色有c种。现从口袋中取出一个巧克力,若取出的
巧克力与桌上已有巧克力颜色相同,则将两个巧克力都取走,否则将取出的巧克力放在桌上。
设从口袋中取出每种颜色的巧克力的概率均等。求取出 n 个巧克力后桌面上剩余 m 个巧克
力的概率。
首先m的个数一定小于等于c,因为如果某种颜色的巧克力数量是大于等于2,那么一定会两个一对被取走,也就是最后剩下的每种巧克力要么只有一个要么没有。如果(n-m)%2!=0,或者m>n,m>n,那么概率一定为0.
那么这个问题其实就可以用概率与期望DP来解决。
令 f[i,j] 表示取出i块巧克力,恰好剩j块的概率。
f[i,j]=f[i−1][j−1]∗c−(j−1)c+f[i−1][j+1]∗j+1c
但是这样做的时间复杂度太高了,那么有没有更高效的方法呢?其实是有的,那就是生成函数。
鉴于这是第一道生成函数的题,所以接下来先说明一些生成函数的预备知识。
指数型生成函数一般用来解决排列问题.定义一个序列{ai}的生成函数为
然后回到这道题的题目。这道题时间上就是让m种颜色取奇数个,c-m种颜色取偶数个,求排列的个数。
根据上面的预备知识我们知道,奇数项指数生成函数为 ex−e−x2 ,偶数项生成函数为 ex+e−x2 ,因此选m个奇数,c-m个偶数的指数生成函数为 (ex−e−x)m(ex+e−x)c−m2c ,由于哪m种颜色取奇数个是不确定的,所以方案数还要乘上 Cmc ,然后再除以总方案数 cn 就是概率。
本题的答案就是多项式 (ex−e−x)m(ex+e−x)c−m∗Cmc2c∗cn 的 xn 项的系数乘以 n!
这里补充一点, ekx 中 xn 的系数为 knn! ,这个吧我其实不会证明,会了再来填坑。
现在考虑如果将 (ex−e−x)m(ex+e−x)c−m 展开得到每个 ekx 中 xn 的系数 gn 。
设 a=ex,b=e−x ,那么根据二项式定理
(a−b)m=∑mr=0(−1)rCrn∗an−r∗br , (a+b)m=∑mr=0Crn∗an−r∗br
因为x,-x会互相抵消,所以我们枚举 (ex−e−x)m 中 ex 的指数i,那么 e−x 的指数为m-i,再枚举 (ex+e−x)c−m 中 ex 的指数j,那么 e−x 的指数为c-m-j,由这两项合并得到的 ekx 中k的值为 2∗(i+j)−c ,然后根据 m−i 的奇偶性确定正负,对答案的贡献就是 knn!∗Cim∗Cjc−m∗(−1)m−i ,我们发现外层还有一个 n!cn ,可以约分得到 (kc)n∗Cim∗Cjc−m∗(−1)m−i .
如果 T=∑(kc)n∗Cim∗Cjc−m∗(−1)m−i , 那么答案就是 T∗Cmc2c
#include
#include
#include
#include
#include
#define N 100
using namespace std;
double C[N+3][N+3];
int c,m,n;
double quickpow(double num,int x)
{
double ans=1; double base=num;
while (x) {
if (x&1) ans=ans*base;
x>>=1;
base=base*base;
}
return ans;
}
int main()
{
freopen("a.in","r",stdin);
for (int i=0;i<=N;i++) C[i][0]=1;
for (int i=1;i<=N;i++)
for (int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
while (true) {
scanf("%d",&c);
if (!c) break;
scanf("%d%d",&n,&m);
if ((n-m)%2||m>c||m>n) {
printf("0.000\n");
continue;
}
double ans=0;
for (int i=0;i<=m;i++)
for (int j=0;j<=c-m;j++) {
double k=2.0*(i+j)-c;
if ((m-i)&1) ans-=quickpow(k*1.0/c,n)*C[m][i]*C[c-m][j];
else ans+=quickpow(k*1.0/c,n)*C[m][i]*C[c-m][j];
}
ans/=quickpow(2.0,c);
ans*=C[c][m];
printf("%.3lf\n",ans);
}
}