AcWing 886. 求组合数 II
费马小定理
若 \(a\) 不是 \(p\) 的整数倍,则有\(a^{p-1}\equiv1\,(mod\, p)\tag{1}\)
乘法逆元
\(b\) 与 \(m\) 互质,则 \(\frac{a}{b}=a*x\,(mod\,m)\) ,其中 \(x\) 称作 \(b\) 模 \(m\) 的逆,记作\(\,b^{-1}\,\),即:
\[\frac{a}{b}=a*b^{-1}\,(mod\,m)
\]
两边同时乘 \(a\) 除 \(b\) ,得:
\[1=b*b^{-1}\,(mod\,m)\tag{2}
\]
且因为 \(b\) 与 \(m\) 互质,所以 \(b\) 非 \(m\) 整数倍,则根据费马小定理(公式1),得:
\[b^{m-1}\equiv1\,(mod\,m)\tag{3}
\]
根据公式(2)和公式(3)得:
\[b^{m-1}=b*b^{-1}
\]
即:
\[b^{-1}=b^{m-2}
\]
故 \(b\) 与 \(m\) 互质, \(b\) 模 \(m\) 的乘法逆元为 \(b^{-1}=b^{m-2}\)
组合数
\(C_n^m=\frac{n!}{m!(n-m)!}=n!*m!^{-1}*(n-m)!^{-1}\)
补充
\((n+1)!^{-1}\) 可以看作 \(\frac{1}{(n+1)!}\),同理,有
\((n)!^{-1}\) 可以看作 \(\frac{1}{(n)!}\)
又因为
\[\frac{1}{(n+1)!}=\frac{1}{n!}*\frac{1}{n+1}
\]
所以
\[(n+1)!^{-1}=n!^{-1}*(n+1)^{-1}
\]
则阶乘和阶乘的逆元都可以在线性复杂度中一同处理
Tips
- 本题目\(\,mod\,\)为素数,则模\(\,mod\,\)的乘法逆元可用快速幂求取
- 改 \(long\;long\) 防止越界
参考
y总:https://www.acwing.com/activity/content/code/content/53394/
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 100010;
const int mod = 1e9+7;
ll fac[N],inv[N];//fac[]:阶乘数组 inv[]:阶乘的逆元数组
int n;
//快速幂来求逆元
ll qmi(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;
}
void init() {
fac[0]=1;//0!=1
inv[0]=1;//0!=1 的逆元 = 1
for(ll i=1;i<=100000;++i) {
fac[i]=fac[i-1]*i%mod;
inv[i]=inv[i-1]*qmi(i,mod-2,mod)%mod;
}
}
int main() {
init();
scanf("%d",&n);
while(n--) {
int a,b;
scanf("%d%d",&a,&b);
printf("%lld\n",fac[a]*inv[b]%mod*inv[a-b]%mod);
}
return 0;
}