组合数
组合数是从 中取 个数。它的表示方式 和 。
阶乘公式:
递归公式:
递归公式可以看作新来的一个苹果,我要么选它要么不选它。
Lucas 定理:
几种 case
当 时,用递归公式预处理。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2010, mod = 1e9 + 7;
int p[N][N];
void init(){
for (int i = 0; i < N; i ++)
for (int j = 0; j <= i; j ++){
if (!j) p[i][j] = 1;
else p[i][j] = (p[i - 1][j] + p[i - 1][j - 1]) % mod;
}
}
int main(){
int n; cin >> n;
init();
while (n -- ){
int a, b; cin >> a >> b;
cout << p[a][b] << endl;
}
}当 时,直接预处理 和 的阶乘和逆元阶乘,其公式为:
,其中逆元用快速幂可求。
当 ,由于 和 过大,在预处理这俩的阶乘就不合适了,而且 也不一定是质数,所以无法用费马小定理和快速幂求逆元 ,应使用 lucas 定理和朴素公式。求组合数公式:
当 ,且不再 mod 谁,也就是说要求一个很大的数,应使用高精度来存储。再观察一下此式:
若对分子和分母分解质因子,对质因子处理,最后相乘即可。需要使用线性筛分解质因子。但分解完质因子,如何求其幂,有个公式是这样的(:( 怎么公式折磨多)):
这个最好在纸上画一遍。举个例子,假如求 中 的次数,大致意思是,除以 ,可以对 都去除一次 ,则幂为 ,除以 ,可以对 去除一次 ,则幂为 ,相加为 。
组合数应用
卡特兰数的应用挺多的。其表达形式:
它的意思是说, 是全部可能情况,而 是不合法情况。二者做差就是合法情况。
满足条件的01序列
给定 个 和 个 ,它们将按照某种顺序排成长度为 的序列,求它们能排列成的所有序列中,能够满足任意前缀序列中 的个数都不少于 的个数的序列有多少个。输出的答案对 取模。

总结
组合数还是挺重要的,掌握基础的组合数是如何计算的以及卡特兰数的应用还是必须的。
最后更新于