组合数之小球与盒子
Colin_Lovecraft · · 个人记录
组合数公式:
组合数也可以由递推(杨辉三角)求出.
好了让我们来看题:
很久很久以前,有
1.球相同,盒子不同,不能有空盒
我们经过一波巧妙地的分析之后,发现这个问题的实质就是把 n 个小球分为 m 组(不能空),然后就这么转化成了一个组合数问题.
其实这就是一个插板法,就是在 n 个小球的 n-1 个空隙当中刚好插入 m-1 个板子,即恰好分为不为空的 m 组.
因为盒子不相同,所以(1,2)和(2,1)是不同的.
2.球相同,盒子不同,可以有空盒
对于每个盒子,我们都给他一个球,那么上一个问题就和这问题一样了,所以我们可以看做自己有 n+m 个小球,然后我们在排列完之后在每一组都删去一个小球,这样就能枚举出有空盒的情况了。于是答案为
3.球不同,盒子不同,可以有空盒
对于每一个球,你都可以放到 1~m 的任意一个位置,由于球不同,所以球与球之间是独立的。因为每个球有 m 种情况,所以我们得出
4.球不同,盒子相同,不能有空盒
对于这个问题有一种神奇的东西叫做第二类斯特林数(不是加特林).
在数学领域,斯特林数分为第一类斯特林数和第二类斯特林数,我们在这里只说第二种.
第二类斯特林数(简称为S2)的定义为将n个物体划分成k个非空的没有区别的集合的方法数,大致就是把 n 个不同的小球放入m个相同的盒子中(且盒子不能为空)的方案数。递推公式为
(
于是我们就可以得出上面的递推公式了,代码如下:
int n = read(),m = read();
S2[0][1] = 1;
for(int i = 0;i <= n;i++){
for(int j = 1;j <= i;j++){
S2[i][j] = (S2[i - 1][j] * j + S2[i - 1][j - 1]);
}
}
cout<<S2[n][m]<<endl;
这个公式是
如果集合没有非空的限制,方法有
我们枚举为空的盒子,可得出
但可能会重复,我们需要套一个容斥即
最后除一个
据说可以用FFT(快速傅里叶变换)加速,但是我太菜了不会,于是不提.
5.球不同,盒子也不同,不能有空盒
事实上这种情况与上面唯一的不同就是有序性,我们只需要在那个公式里不除
6.球不同,盒子相同,可以有空盒
因为可以有空盒,我们可以枚举每次一共用了几个盒子,然后把相应的第二类斯特林数加起来就可以了.
似乎这种数的名字叫做贝尔数.
Bell数的定义:第n个Bell数表示集合{1,2,3,...,n}的划分方案数.
看起来是不是和第二类斯特林数有点像.
可以说贝尔数就是其对应的第二类斯特林数之和.
贝尔数的相应公式为
也可以通过对应的第二类斯特林数求和得到,公式如下.
7.球相同,盒子相同,可以有空盒
设
如果只有一个盘子或者没有小球,方案数自然为
如果小球比盒子要少,小球肯定是放不满盒子的,由于盒子相同,可以得到转移
总结上面三种情况就可以得出转移方程了,代码如下
int n = read(),m = read();
for(int i = 0;i <= n;i++){
for(int j = 1;j <= m;j++){
if(j == 1 || i == 0) f[i][j] = 1;
else if(i < j) f[i][j] = f[i][i];
else if(i >= j) f[i][j] = f[i - j][j] + f[i][j - 1];
}
}
printf("%d\n",f[n][m]);
8.球相同,盒子相同,不能有空盒
有点类似于第一、二种情况之间的关系.
我们先假设在每一个盒子里都放上了一个球,就跟上面的情况一样了..