0. 写在前面
在程序设计中,可能会碰到多种类型的计数问题,其中不少涉及到组合数的计算,所以笔者写下这么一篇文章,期望能解决一些常规的组合数求模问题。以下部分内容改编自
AekdyCoin
的《组合数求模》,而且为了感谢他对(懵懂的)笔者的启发,这篇文章的标题与其文章相同。另外,感谢
Picks
将多项式运算的技巧在中国进行推广,感谢
51nod提供了许多有趣的数论题目,感谢
fotile96
开源了
他的FFT模板。
在接下来的内容中,文章将围绕
C(n,m)modp进行分析,其中
C(n,m)表示从
n个人中选出
m个人跪
sd0061
的方案数。
author: skywalkert
original article: http://blog.csdn.net/skywalkert/article/details/52553048
last update time : 2017-03-30
1. 枚举法
这一部分的内容主要是利用基本定义
C(n,m)=n!m!(n−m)!=n(n−1)⋯(n−m+1)1⋅2⋯m来进行分析的。
1.1 直接枚举
如果
n−m<m,不妨将
n−m视作
m,所以我们只需要考虑
m≤n2的情况。
若
1,2,⋯,m在模
p意义下有乘法逆元,则考虑直接计算分子、分母在模意义下的值,利用扩展欧几里得算法
O(logp)求逆即可,这样做求
C(n,m)的复杂度是
O(m)的。注意
p可以不是质数。
如果需要求
C(n,1),C(n,2),⋯,C(n,m),也可以利用上述方法做到
O(mlogp),这是因为
C(n,m)=C(n,m−1)⋅n−m+1m
如果能提前
O(m)预处理
1,2,⋯,m的逆元,则上述问题可以做到
O(m),这是因为
m=x⋅⌊mx⌋+(mmodx)⇒x⋅⌊mx⌋+(mmodx)≡0(modm)⇒x−1≡−⌊mx⌋(mmodx)−1(modm)
注意,当
m不为质数时,
(mmodx)不一定与
m互质,所以上述做法需要有
m是质数的条件。
1.2 预处理
由于
C(n,m)=n(n−1)⋯(n−m+1)1⋅2⋯m=(n−1)⋯(n−m+1)1⋅2⋯(m−1)(1+n−mm)=C(n−1,m−1)+C(n−1,m),