【组合数学】 卢卡斯定理详解(证明+模板)

一.引入

1.组合数 C n m C_n^m Cnm是一类增长速度非常快的数, C 300 150 C_{300}^{150} C300150就已经是一个90位的数了,当m,n取 1 0 9 10^9 109级别甚至更大的数时,其组合数肯定难以计算了,所以做题时常常会需要组合数对一个数取模。而卢卡斯定理在这种情况下就派上了用场。

2.卢卡斯定理的应用:在m,n很大的时候,快速求解 C n m % p C_n^m\%p Cnm%p

3.卢卡斯定理的具体表述:

C n m = C a 0 b 0 ⋅ C a 1 b 1 ⋅ C a 2 b 2 ⋅ ⋅ ⋅ C a k b k C^m_n=C^{b_0}_{a_0}\cdot C^{b_1}_{a_1}\cdot C^{b_2}_{a_2}\cdot\cdot\cdot C^{b_k}_{a_k} Cnm=Ca0b0Ca1b1Ca2b2Cakbk(mod p) = ∏ i = 0 k C a i b i =\prod_{i=0}^{k} C^{b_i}_{a_i} =i=0kCaibi (mod p)

(mod p)表示只是在模p的条件下成立

其中 n = a 0 + a 1 p + a 2 p 2 + ⋅ ⋅ ⋅ + a k p k n=a_0+a_1p+a_2p^2+\cdot\cdot\cdot+a_kp^k n=a0+a1p+a2p2++akpk, m = b 0 + b 1 p + b 2 p 2 + ⋅ ⋅ ⋅ + b k p k m=b_0+b_1p+b_2p^2+\cdot\cdot\cdot+b_kp^k m=b0+b1p+b2p2++bkpk

即将m,n按p进制展开

4.卢卡斯定理的等价式子:

这个式子和上面的式子是完全等价的,而且也是在编程中更常用的,因为可以通过函数递归来实现:

C n m = C n % p m % p ⋅ C n / p m / p C_n^m=C_{n\%p}^{m\%p}\cdot C_{n/p}^{m/p} Cnm=Cn%pm%pCn/pm/p ( mod p)

这里的/是向下取整的

证明很简单,和进制转换的模n取余法是一样的:

n = a 0 + a 1 p + a 2 p 2 + ⋅ ⋅ ⋅ + a k p k n=a_0+a_1p+a_2p^2+\cdot\cdot\cdot+a_kp^k n=a0+a1p+a2p2++akpk

n%p即 a 0 a_0 a0 ,因为多项式中其他的项都至少含有一个p,也就是p的倍数。

n/p即 a 1 + a 2 p + a 3 p 2 + ⋅ ⋅ ⋅ + a k p k − 1 a_1+a_2p+a_3p^2+\cdot\cdot\cdot+a_kp^{k-1} a1+a2p+a3p2++akpk1

不断重复上述过程即将, C n m C_n^m Cnm转化成了卢卡斯定理表达的那样。

可以发现,在经过卢卡斯定理的化简后,我们只需要不断求 C n % p m % p C_{n\%p}^{m\%p} Cn%pm%p即可,大大减小了运算量。

二.卢卡斯定理的证明

1.同余:

两个整数a、b,若它们除以整数m所得的余数相等,则称a与b对于模m同余。

记作:a≡b (mod m),

2.证明 ( 1 + x ) p (1+x)^p (1+x)p 1 + x p 1+x^p 1+xp(mod p) (前提:p为素数)

①将 ( 1 + x ) p (1+x)^p (1+x)p按二项式展开,得:

( 1 + x ) p = C p 0 x 0 + C p 1 x + C p 2 x 2 + C p 3 x 3 + ⋅ ⋅ ⋅ + C p k x k + ⋅ ⋅ ⋅ + C p p x p (1+x)^p=C^0_px^0+C^1_px+C^2_px^2+C^3_px^3+\cdot\cdot\cdot+C^k_px^k+\cdot\cdot\cdot+C^p_px^p (1+x)p=Cp0x0+Cp1x+Cp2x2+Cp3x3++Cpkxk++Cppxp

其中 C p 0 x 0 = 1 , C p p x p = x p C^0_px^0=1, C^p_px^p=x^p Cp0x0=1,Cppxp=xp

所以我们可以将这个式子写成:

1 + C p 1 x + C p 2 x 2 + C p 3 x 3 + ⋅ ⋅ ⋅ + C p k x k + ⋅ ⋅ ⋅ + x p 1+C^1_px+C^2_px^2+C^3_px^3+\cdot\cdot\cdot+C^k_px^k+\cdot\cdot\cdot+x^p 1+Cp1x+Cp2x2+Cp3x3++Cpkxk++xp

②由 C p i ( i ∈ [ 1 , n ] ) = p ! i ! ⋅ ( p − i ) ! C_p^i (i∈[1,n])=\frac{p!}{i!\cdot(p-i)!} Cpi(i[1,n])=i!(pi)!p!可得,当p为素数时,由于其分子中含有p,其必然是p的倍数,即 C p i C_p^i Cpi%p==0。(注:若p为合数,则将被分母中的数约掉,这也解释了卢卡斯定理为什么要求p必须为素数)

所以除了第一项和最后一项(也就是1和 x p x^p xp), ( 1 + x ) p (1+x)^p (1+x)p的二项展开式中其他几项都%p==0。

③同余相加定理:

若a≡b(mod m),c≡d(mod m),则a+c≡b+d(mod m)

因为 C p i C_p^i Cpi%p=0 ,可以记为 C p i C_p^i Cpi≡0 (mod p)

所以二项展开式中间的几项在%p的意义上其实相当于0

所以:

( 1 + x ) p (1+x)^p (1+x)p 1 + x p 1+x^p 1+xp(mod p)

证毕

3.证明 C n m = C n % p m % p ⋅ C n / p m / p C_n^m=C_{n\%p}^{m\%p}\cdot C_{n/p}^{m/p} Cnm=Cn%pm%pCn/pm/p ( mod p)

注:上面的/是计算机意义上的除以,即舍弃余数(向下取整)

令n=sp+q , m=tp+r

其中q,r就是n和m 除以p 的余数
而s,t就是n,m除以p后截断的部分(向下取整)

那么我们要证明的就是 C n m = C q r ⋅ C s t C_n^m=C_q^{r}\cdot C_{s}^{t} Cnm=CqrCst( mod p) :

对于 ( 1 + x ) n (1+x)^n (1+x)n, C n m C^m_n Cnm就是其指数为 x m x^m xm的项的系数

① 先化简:

( 1 + x ) n = ( 1 + x ) s p + q = [ ( 1 + x ) p ] s ⋅ ( 1 + x ) q (1+x)^n=(1+x)^{sp+q}=[(1+x)^p]^s\cdot(1+x)^q (1+x)n=(1+x)sp+q=[(1+x)p]s(1+x)q

在同余意义下:

上 式 ≡ ( 1 + x p ) s ⋅ ( 1 + x ) q 上式≡(1+x^p)^s\cdot(1+x)^q (1+xp)s(1+x)q ( mod p)

③再将 ( 1 + x p ) s ⋅ ( 1 + x ) q (1+x^p)^s\cdot(1+x)^q (1+xp)s(1+x)q二项式展开得:

[ 1 + C s 1 x p + C s 2 x 2 p + C s 3 x 3 p + ⋅ ⋅ ⋅ ] ⋅ [ 1 + C q 1 x + C q 2 x 2 + C q 3 x 3 + ⋅ ⋅ ⋅ ] [1+C^1_sx^p+C^2_sx^{2p}+C^3_sx^{3p}+\cdot\cdot\cdot]\cdot[1+C^1_qx+C^2_qx^2+C^3_qx^3+\cdot\cdot\cdot] [1+Cs1xp+Cs2x2p+Cs3x3p+][1+Cq1x+Cq2x2+Cq3x3+]

④现在我们要得到指数为 x m x^m xm的项的系数:

m=tp+r,所以 x m = x t p ⋅ x r x^m=x^{tp}\cdot x^{r} xm=xtpxr,因为q是一个小于p的数,所以后一个多项式,即 ( 1 + x ) q (1+x)^q (1+x)q不可能提供指数大于p的项。同理,前一个多项式,即 ( 1 + x p ) s (1+x^p)^s (1+xp)s,不可能提供指数小于p的项。

所以, x m x^m xm的系数必然为前一个多项式中 x t p x^{tp} xtp的系数乘以后一个多项式的 x r x^{r} xr系数,也就是 C q r ⋅ C s t C_q^{r}\cdot C_{s}^{t} CqrCst

⑤综上,得证 C n m = C n % p m % p ⋅ C n / p m / p C_n^m=C_{n\%p}^{m\%p}\cdot C_{n/p}^{m/p} Cnm=Cn%pm%pCn/pm/p ( mod p)

三.模板

long long Lucas(long long n,long long m){
    if(m==0)  return 1;
    return Lucas(n/p,m/p)*C(n%p,m%p)%p;
}

每次只要求 C n % p m % p C_{n\%p}^{m\%p} Cn%pm%p,大大减少了计算量,至于这里的C就是计算组合数的函数

具体如下:

long long C(long long n,long long m){
    if(n<m)      return 0;
	if(m>n-m)     m=n-m;
	long long  a=1,b=1;
	for(int i=0;i<m;i++){
		a=(a*(n-i))%p;
		b=(b*(i+1))%p;
	}
	return a*quickpow(b,p-2)%p;
}

C n m = n ⋅ ( n − 1 ) ⋅ ( n − 2 ) ⋅ ⋅ ⋅ ( n − m + 1 ) 1 ⋅ 2 ⋅ 3 ⋅ ⋅ ⋅ ⋅ m C_n^m=\frac{n\cdot(n-1)\cdot(n-2)\cdot\cdot\cdot(n-m+1)}{1\cdot2\cdot3\cdot\cdot\cdot\cdot m} Cnm=123mn(n1)(n2)(nm+1)
a代表分子,不断累乘至n-m+1
b代表分母,不断累乘至m

a/b的模即a×b的乘法逆元的模,b的乘法逆元即 b p − 2 b^{p-2} bp2

quickpow为快速幂模板

完整模板:

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;

long long n,m,p;

long long quickpow(long long base,long long power){
   long long ret=1;
   while(power){
      if(power%2)
         ret=ret*base%p;
      base=base*base%p;
      power/=2;
   }
   return ret;
}

long long C(long long n,long long m){
    if(n<m)      return 0;
	if(m>n-m)     m=n-m;
	long long  a=1,b=1;
	for(int i=0;i<m;i++){
		a=(a*(n-i))%p;
		b=(b*(i+1))%p;
	}
	return a*quickpow(b,p-2)%p;
}

long long Lucas(long long n,long long m){
    if(m==0)  return 1;
    return Lucas(n/p,m/p)*C(n%p,m%p)%p;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld%lld",&n,&m,&p);
        printf("%lld\n",Lucas(n,m));
    }
    return 0;
}

四.时间复杂度分析

Lucas函数调用 l o g p m log_pm logpm次,因为该函数的递归出口是m==0,每次调用m都除以p

每调用一次Lucas函数都会调用一次C函数,C函数内部是一个for循环,复杂度O(p),因为该函数里的m是模p后的结果,值为1~p

快速幂quickpow函数复杂度为 l o g 2 p log_2p log2p

综上,时间复杂度为O( p ⋅ l o g p m ⋅ l o g 2 p p\cdot log_pm\cdot log_2p plogpmlog2p)

后两者都是常数级别,所以主要取决于p,p不能太大,一般 1 0 5 10^5 105以内都可

优化:若p已知,则可以O(p)的复杂度预处理阶乘,C函数写法稍有变化:

void fact(){
  fact[0]=1;
  for(int i=1;i<=p;i++)
    fact[i]=fact[i-1]*i%p;
}

long long inv(long long x){
    return quickpow(x,p-2);
}

long long C(long long n,long long m){
  if(n<m)  return 0;
  return fact[n]*inv(fact[m])*inv(fact[n-m])%p;
}

五.练手题目

  1. 洛谷模板题
  2. Saving Beans
  3. 机器人走方格V3
  • 36
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值