一、引入
给定
n ,
m ,
p ,求:
(mn)modp
其中
(mn) 为组合数,表示
n 个元素中选出
m 个的方案数。
即:
(mn)=m!×(n−m)!n!
特殊地,我们规定
(0n)=1 且当
n<m 时
(mn)=0 。
二、
n,m≤3,000
(mn)=(mn−1)+(m−1n−1)
(1)组合意义:
考察
n 个元素中的最后一个元素是否被选出。
如果没有被选出,那么前面的
n−1 个元素必须选出
m 个,即
(mn−1) 。
如果被选出,那么前面的
n−1 个元素必须选出
m−1 个,即
(m−1n−1) 。
(2)数学推导:
(mn−1)+(m−1n−1)=m!×(n−1−m)!(n−1)!+(m−1)!×(n−m)!(n−1)!
=m!×(n−m)!(n−1)!×(n−m)+(n−1)!×m=m!×(n−m)!n!=(mn)
(3)生成函数:
根据二项式定理
(a+b)n=∑i=0n(in)aibn−i ,数列
(0n),(1n),(2n),...,(nn)
的生成函数为
(1+x)n 。
于是
(mn) 就是
(1+x)n 的
m 次项。
设
(F(x))k 表示多项式
F(x) 的
k 次项。
则:
(mn−1)+(m−1n−1)=((1+x)n−1)m+((1+x)n−1)m−1
=((1+x)n−1)m+(x(1+x)n−1)m=((1+x)n)m=(mn)
利用这个公式进行
O(nm) 的递推,可以预处理出
i∈[0,n] ,
j∈[0,m] 范围内所有的
(ji) 。
当
n 达到
109 甚至
1018 级别但
m 只有
100 级别时,可以用矩阵乘法优化这个递推,复杂度可以做到
O(m3logn) 。
三、
n,m≤107 ,
n,m<p 且
p 是质数
直接使用组合数的通项公式:
(mn)=m!×(n−m)!n!
预处理阶乘极其逆元即可。
逆元线性递推公式:
x−1≡(p−⌊xp⌋)×(pmodx)−1(modp)
(x!)−1=i=1∏xi−1
复杂度
O(max(n,m)) 。
四、
p≤107 且为质数
Lucas 定理:
(mn)≡(⌊pm⌋⌊pn⌋)×(mmodpnmodp)(modp)
另一种说法:
(mn)=i=0∏∞(mini)(modp)
其中
ni 表示
p 进制意义下
n 第
i 位的值,最低位为第
0 位。
证明:
前面已经提到,数列
(0n) ,
(1n) ,
… ,
(nn) 的生成函数是
(1+x)n 。
为了方便讨论,下面多项式的系数都在模
p 意义下。
我们知道,当
0<n<p 时:
(np)≡0(modp)
于是:
(1+x)n≡1+xn(modp)
根据
n=⌊pn⌋×p+nmodp ,得到:
(1+x)n=((1+x)p)⌊pn⌋×(1+x)nmodp
≡(1+xp)⌊pn⌋×(1+x)nmodp(modp)
利用二项式定理展开:
i=0∑n(in)xi≡(i=0∑⌊pn⌋(i⌊pn⌋)(xp)i)(i=0∑nmodp(inmodp)xi)
式子左边的
m 次项为
(mn) 。
式子右边是两个多项式的卷积,但是第一个多项式只有
p 的倍数次项不为
0 ,第二个多项式大于等于
p 的项全部为
0 。
由于将整数
m 表示成
m=kp+c 且
c<p 的方式是唯一的,即
k=⌊pm⌋ ,
c=mmodp ,
所以这两个多项式卷积的
m 次项系数为第一个多项式的
⌊pm⌋ 项系数乘上第二个多项式的
mmodp 次项系数。
即式子右边的
m 次项为
(⌊pm⌋⌊pn⌋)×(mmodpnmodp) 。
得证。
先预处理出
[0,min(max(n,m),p−1)] 内的阶乘及其逆元。
然后根据 Lucas 定理递归下去,当
n,m 都小于
p 时停止递归。
复杂度
O(min(max(n,m),p−1)+logp) 。
五、
n,m≤107 ,
p≤109
这时候
p 不一定是质数。
先把
n!,m!,(n−m)! 分解质因数。
阶乘分解质因数的方法:
n! 中质因子
k 的出现次数为:
i=1∑∞⌊kin⌋
然后
(mn) 中质因子
k 的出现次数为
n! 中
k 的出现次数减去
m! 中
k 的次数再减去
(n−m)! 中
k 的次数。
复杂度
O(n+m) 。
六、
n,m≤109 ,
pici≤106
其中
pi 和
ci 分别表示
p 的质因子和
pi 在
p 中的次数。
扩展 Lucas 定理。
先把
p 分解质因数:
p=i=1∑spici
如果我们求出了对于每个
i ,
(mn)modpici 的值,就可以使用中国剩余定理合并,得到
(mn) 。
下面的关键就是求
(mn)modpici 。
由于
pi 是质数,所以我们只需要先使用阶乘分解算出
(mn) 中
pi 的次数
cnt ,
分别求出
n! 、
m! 、
(n−m)! 去掉所有质因子
pi 之后模
pici 的结果,记为:
F(n),F(m),F(n−m)
那么:
(mn)≡F(n)×F(m)−1×F(n−m)−1×picnt(modpici)
下面重点讨论求
F(n) 。
先
i 从
0 到
pici−1 预处理出
N(i) 表示
1 到
i 所有不是
pi 的倍数的数的积模
pici 。
可以递归求
F(n) 。递归边界为
n<pi ,这时
F(n)=N(n) 。
否则取出
1 到
n 中所有
pi 的倍数先处理,可以得出这就是
F(⌊pin⌋) 。
然后考虑
1 到
n 内每个长度为
pici 的循环节,这个循环节内数的乘积是一样的,
所以这一部分的贡献为:
N(pici−1)⌊picin⌋
还有剩下的
nmodpici 个数无法构成循环节,所以最后一部分的贡献为
N(nmodpici) 。
所以:
F(n)={F(⌊pin⌋)×N(pici−1)⌊picin⌋×N(nmodpici)N(n)n≥pin<pi
一个有趣的栗子:
n=19,pi=3,ci=2 。
19!=(1×2×4×5×7×8)×(10×11×13×14×16×17)
×19×36×6!
≡(1×2×4×5×7×8)2×19×36×6!(mod9)
上面
1×2×4×5×7×8 即为
N(9−1) ,
6! 可以递归下去,
19 即为剩余的
N(19mod9)=N(1) 。
复杂度比较迷……
七、
n,m≤109 ,
p 是 int 范围内的质数
如果有某个 dalao 会复杂度比较优秀的做法,请在评论区发表。