卢卡斯

C(n, m) % p = (C(n/p, m/p) % p) * (C(n%p, m%p) % p) % p

其实就是这个公式啦!用于组合数求模(通常用于阶乘无法解决的问题)

在此之前,要先了解逆元

inv[i]=-p/i*inv[p%i],inv[i]是在%p的条件下i的逆元

逆元的阶乘是这么求的

void work() {
    scanf("%d%d%d", &n, &m, &p);
    A[0] = B[0] = A[1] = B[1] = 1;
    n += m;
    for (int i = 2; i <= p; i++)
        B[i] = -(LL)(p/i)*B[p%i]%p;//逆元,为下面求阶乘 
    for (int i = 2; i <= p; i++)
        A[i] = (LL)A[i-1]*i%p,//阶乘 
        B[i] = (LL)B[i-1]*B[i]%p;//逆元的阶乘 
    printf("%d
", (Lucas(n, m, p)+p)%p);
}

然后就是卢卡斯的板子了(洛谷3807)

#include<bits/stdc++.h>
#define LL long long

using namespace std;
const int N = 1e5;
int n, m, p;
int A[N+5], B[N+5];
int C(int n, int m, int p) {
    if (m > n) return 0;//c(n,m)组合应该是上面的数小,下面的数大 
    return (LL)A[n]*B[n-m]%p*B[m]%p;//n!/(n-m)!m! 
}
int Lucas(int n, int m, int p) {//卢卡斯m,n大于P时,就不能保证m,n与P互质了,
//但不互质的情况下,乘法逆元不存在,此时就需要卢卡斯定理来减小m,n的规模
    if (!m) return 1;//m都已经==0了 
    return (LL)C(n%p, m%p, p)*Lucas(n/p, m/p, p)%p;
    //公式Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p) 
}
void work() {
    scanf("%d%d%d", &n, &m, &p);
    A[0] = B[0] = A[1] = B[1] = 1;
    n += m;
    for (int i = 2; i <= p; i++)
        B[i] = -(LL)(p/i)*B[p%i]%p;//逆元,为下面求阶乘 
    for (int i = 2; i <= p; i++)
        A[i] = (LL)A[i-1]*i%p,//阶乘 
        B[i] = (LL)B[i-1]*B[i]%p;//逆元的阶乘 
    printf("%d
", (Lucas(n, m, p)+p)%p);
}
int main() {
    int t;
    scanf("%d", &t);
    while (t--)
        work();
    return 0;
}
原文地址:https://www.cnblogs.com/yyys-/p/10498410.html