Lucas定理及其应用

概述:

\(Lucas\) 定理用于求解大组合数取模的问题,其中 \(p\) 必须为素数。

\(Lucas\) 定理内容如下:

\[C_{n}^{m}=C_{(n/p)}^{(m/p)} * C_{(n\%p)}^{(m\%p)}(mod\ p) \]

观察上述表达式,可知 \(n\%p\)\(m\%p\) 一定是小于 \(p\) 的数,可以直接求解。
\(C_{(n/p)}^{(m/p)}\) 可以继续用 \(Lucas\) 定理求解,这也就要求 \(p\) 的范围不能够太大,一般在 \(1e5\) 左右。
边界条件:当 \(m=0\) 的时候,返回 \(1\)
复杂度:\(O(logn+p)\)

模板代码【P3807】:

//1≤n,m,p≤10^5,1≤T≤10,p为素数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll power(ll a,ll b,ll p)
{
    ll res=1;
    while(b)
    {
        if(b&1)
            res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res%p;
}
ll C(ll n,ll m,ll p)
{
    if(m>n)
        return 0;
    ll a=1,b=1;
    m=min(m,n-m);
    while(m)
    {
        a=a*(n-m+1)%p;
        b=b*m%p;
        m--;
    }
    return a*power(b,p-2,p);
}
ll Lucas(ll n,ll m,ll p)
{
    return m?C(n%p,m%p,p)*Lucas(n/p,m/p,p)%p:1;
}
int main()
{
    int t;
    scanf("%d",&t);
    ll n,m,p;
    while(t--)
    {
        scanf("%lld%lld%lld",&n,&m,&p);
        printf("%lld\n",Lucas(n+m,m,p));
    }
    return 0;
}

具体证明

posted @ 2020-04-06 15:19  xzx9  阅读(218)  评论(0编辑  收藏  举报