传送门
题面:包裹里有无限个分布均匀且刚好(c)种颜色的巧克力,现在要依次拿(n)个出来放到桌子上,每次如果桌子上面有两种相同颜色的巧克力就会把这两个巧克力给吃掉,求最后桌子上面还有(m)个巧克力的概率。(来自博主HopeForBetter的翻译,谢谢)
这题更是让我感受到了暴力在数学题中的用处。
首先,剩(m)个颜色,那就说明有(m)个颜色拿了奇数个,有(c-m)个颜色拿了偶数个。那么对应的生成函数就是
[f(x)=(frac{e^x+e^{-x}}{2})^{c-m}(frac{e^x-e^{-x}}{2})^m
]
记这个函数的(n)次项系数为(k),那么答案就是(frac{inom{c}{m} n! k}{c^n}).
那么关键就是如何求该生成函数的(n)次项的系数。先用二项式定理展开,得到
[frac1{2^c} [sum_{i=0}^{c-m}inom{c-m}{i}e^{(2i-c+m)x}sum_{j=0}^m inom{m}{j} (-1)^{m-j}e^{(2j-m)x}]
]
接下来是关键:我们枚举(i,j),那么当(i,j)一定时该生成函数就是(inom{c-m}{i}inom{m}{j}(-1)^{m-j}e^{(2i+2j-c)x})((frac1{2^c})在最后乘上即可,暂时不写),我们将这个展开,得到他的第(n)项,就是(frac{(-1)^{m-j}}{n!}inom{c-m}{i}inom{m}{j}(2i+2j-c)^n),将这个加到答案中即可.
这道题的思路就是这些。实现的时候记得组合数用杨辉三角预处理,然后poj输出是%f,否则会WA.
#include<cstdio>
using namespace std;
#define In inline
typedef double db;
const int maxn = 105;
In db quickpow(db a, int b)
{
db ret = 1;
for(; b; b >>= 1, a = a * a)
if(b & 1) ret = ret * a;
return ret;
}
int c, n, m;
db C[maxn][maxn];
In void init()
{
C[0][0] = 1;
for(int i = 1; i < maxn; ++i)
{
C[i][0] = 1;
for(int j = 1; j <= i; ++j) C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}
}
In db solve()
{
if(m > c || m > n || ((n - m) & 1)) return 0;
db ans = 0;
for(int i = 0; i <= c - m; ++i)
for(int j = 0; j <= m; ++j)
{
db tp = quickpow(1.0 * (2 * i + 2 * j - c) / c, n) * C[c - m][i] * C[m][j];
if((m - j) & 1) ans -= tp;
else ans += tp;
}
return ans / quickpow(2, c) * C[c][m] ;
}
int main()
{
init();
while(scanf("%d", &c) && c)
{
scanf("%d%d", &n, &m);
printf("%.3f
", solve());
}
return 0;
}