Lucas定理 原理、证明及代码模板

一、定理

对于素数p,及正整数a、b,当求组合数 C a b C_a^b Cab对p取余时,如果a和b很大 ( 1 ≤ b ≤ a ≤ 1 0 15 ) (1≤b≤a≤10^{15}) (1ba1015),而p相对较小 ( 1 ≤ p ≤ 1 0 5 ) (1≤p≤10^5) (1p105)时,我们用Lucas定理来求。

结论: C a b ≡ C a p b p ⋅ C a % p b % p C_{a}^{b}\equiv C_{\frac ap}^{\frac bp}\cdot C_{a\%p}^{b\%p} CabCpapbCa%pb%p (mod p)


二、证明

首先将a和b用p进制表示出来
a = a 0 p 0 + a 1 p 1 + . . . + a k p k a=a_0p^0+a_1p^1+...+a_kp^k a=a0p0+a1p1+...+akpk b = b 0 p 0 + b 1 p 1 + . . . + b k p k b=b_0p^0+b_1p^1+...+b_kp^k b=b0p0+b1p1+...+bkpk

然后,在证明前,我们要用到两个引理
(1)p是素数, p ∣ C p k p|C_p^k pCpk,也就是说p是 C p k C_p^k Cpk的一个因子。这个很好证明,因为 C p k = p ! k ! ( p − k ) ! C_p^k=\frac {p!}{k!(p-k)!} Cpk=k!(pk)!p!分子p的阶乘中含有p作为因子,因为p是素数,所以分母中的阶乘都和p互素,那么p就是 C p k C_p^k Cpk的一个因子了,即p能整除 C p k C_p^k Cpk
(2) ( x + y ) p ≡ x p + y p (x+y)^p\equiv x^p+y^p (x+y)pxp+yp (mod p)。这个也很好证明,根据二项式定理将括号拆开可以得 ( x + y ) p = x p + ∑ k = 1 p − 1 C p k x k y p − k + y p (x+y)^p=x^p+\sum _{k=1}^{p-1}C_p^kx^ky^{p-k}+y^p (x+y)p=xp+k=1p1Cpkxkypk+yp,而中间项含有 C p k C_p^k Cpk,是p的整数倍,因此在模p的情况下都可以约掉。就剩下了两边的两项。同理可得: ( x + y ) p k ≡ x p k + y p k (x+y)^{p^k}\equiv x^{p^k}+y^{p^k} (x+y)pkxpk+ypk (mod p)

有了这些准备之后,我们再来证明,根据二项式定理将 ( 1 + x ) a (1+x)^a (1+x)a展开。因为a可以用p进制表示,所以
( 1 + x ) a = ( 1 + x ) a 0 p 0 + a 1 p 1 + . . . + a k p k (1+x)^a = (1+x)^{a_0 p^0+a_1p^1+...+a_kp^k} (1+x)a=(1+x)a0p0+a1p1+...+akpk ( 1 + x ) a = ( 1 + x ) a 0 × ( 1 + x ) a 1 p × . . . × ( 1 + x ) a k p k (1+x)^a = (1+x)^{a_0 }\times (1+x)^{a_1p}\times ...\times (1+x)^{a_kp^k} (1+x)a=(1+x)a0×(1+x)a1p×...×(1+x)akpk ( 1 + x ) a ≡ ( 1 + x ) a 0 × ( 1 + x p ) a 1 × . . . × ( 1 + x p k ) a k (1+x)^a \equiv (1+x)^{a_0 }\times (1+x^p)^{a_1}\times ...\times (1+x^{p^k})^{a_k} (1+x)a(1+x)a0×(1+xp)a1×...×(1+xpk)ak ( 1 + x ) a ≡ ∑ m = 0 a 0 C a 0 m x m × ∑ m = 0 a 1 C a 1 m x p m × . . . × ∑ m = 0 a k C a k m x p k m (1+x)^a \equiv \sum _{m=0}^{a_0}C_{a_0}^mx^m\times \sum_{m=0}^{a_1}C_{a_1}^mx^{pm}\times ...\times \sum_{m=0}^{a_k}C_{a_k}^mx^{p^km} (1+x)am=0a0Ca0mxm×m=0a1Ca1mxpm×...×m=0akCakmxpkm然后我们再看这个同余式左右两边的 x b x^b xb的系数左边 x b x^b xb的系数是 C a b C_a^b Cab。再看右边,右边所有x的幂都和p进制下的展开是一模一样的,而正好b的p进制是 b = b 0 p 0 + b 1 p 1 + . . . + b k p k b=b_0p^0+b_1p^1+...+b_kp^k b=b0p0+b1p1+...+bkpk,并且右边的每一项都是乘起来的,因此要找 x n x^n xn这一项,当且仅当第一个的i取 b 0 b_0 b0,第二个i取 b 1 b_1 b1,…,第k项的i取 b k b_k bk(他们加起来刚好是b的p进制展开,所以幂次就是b),而他们的每一项的系数就是 C a 0 b 0 C_{a_0}^{b_0} Ca0b0 C a 1 b 1 C_{a_1}^{b_1} Ca1b1、… C a k b k C_{a_k}^{b_k} Cakbk。因此 x n x^n xn这一项的系数就是将他们乘起来即 C a 0 b 0 × C a 1 b 1 × . . . × C a k b k C_{a_0}^{b_0}\times C_{a_1}^{b_1}\times ... \times C_{a_k}^{b_k} Ca0b0×Ca1b1×...×Cakbk (mod p)。

因此得证,根据左右两边 x n x^n xn的系数相同可得: C a b ≡ C a 0 b 0 × C a 1 b 1 × . . . × C a k b k C_a^b\equiv C_{a_0}^{b_0}\times C_{a_1}^{b_1}\times ... \times C_{a_k}^{b_k} CabCa0b0×Ca1b1×...×Cakbk (mod p)。时间复杂度大概是 O ( p log ⁡ N log ⁡ p ) O(p\log N\log p) O(plogNlogp)


三、代码模板

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;

int p,n;

int qmi(int a,int b)	//快速幂求逆元
{
    LL res = 1;
    while(b)
    {
        if(b & 1)
            res = (LL)res * a % p;
        a = (LL)a * a % p;
        b >>= 1;
    }
    return res;
}

int C(int a,int b)	//公式求C(a,b)
{
    int res = 1;
    for(int i = 1,j = a;i <= b;i++,j--)
    {
        res = (LL)res * j % p;		//分子从a-b+1乘到a
        res = (LL)res * qmi(i,p-2) % p;	//分母是b的阶乘,转化为乘以阶乘的逆元
    }
    return res;
}

int lucas(LL a,LL b)	//Lucas定理
{
    if(a <p && b < p)	//如果a和b都小于p,可以直接用公式法求c
        return C(a,b);
    return (LL)C(a%p,b%p)*lucas(a/p,b/p) % p;	//否则就用Lucas定理,用Lucas结论来求解
}

int main()
{
    cin >> n;
    while (n -- )
    {
        LL a,b;
        cin >> a >> b >> p;		//输入a、b和模数p
        cout << lucas(a,b) << endl;
    }
    
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值