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; }