求组合数(快速幂求逆元)
费马小定理:
当如果***p是一个质数且整数a不是p的倍数***则有
ap−1≡1a^{p-1} \equiv 1ap−1≡1 (mod p)
乘法逆元定义:
如果整数b,m互质,且对于任意的整数a,如果满嘴b|a,则存在一个整数x满足 a/b ≡\equiv≡ a * x (mod m)
则称x是b的逆元
b存在逆元的充要条件是b与模数m互质,且m为质数 :
bm−2即为b的逆元b^{m-2}即为b的逆元bm−2即为b的逆元
快速幂就可以求出这个逆元了
求组合数
Cab=a!(a−b)!b!C_a^b = \frac{a!}{(a-b)!b!}Cab=(a−b)!b!a!
Cab=Cab−1+Ca−1b−1C_a^b = C_a^{b-1}+C_{a-1}^{b-1}Cab=Cab−1+Ca−1b−1
两个公式的选取取决于是查找的范围大还是ab的范围大
如果查找的范围很大,ab的范围很小,那么直接预处理所有的C的值即可时间复杂度为N*N(ab的范围)
如果ab的范围很大,则用第一条进行预处理,查询则直接查即可,时间复杂度为 NlogN
因为要mod 1e9 + 7 ,除法的mod不好mod,所以这一步需要用到逆元。
注意!!! 0的阶乘是1
#include<iostream>
using namespace std;
int n;
const int N = 100010, mod =1e9 + 7;
int fact[N],infact[N];
typedef long long ll;
int qmi(int a ,int k ,int m)
{
int res = 1;
while(k)
{
if(k & 1)
res = (ll)res * a % m;
a = (ll)a * a % m;
k >>= 1;
}
return res;
}
int main()
{
cin>>n;
fact[0]= infact[0] = 1;
//预处理
for(int i = 1 ;i <= N; i++)
{
fact[i] = (ll)fact[i-1] * i % mod;
infact[i] = (ll)infact[i-1] * qmi(i,mod - 2, mod) % mod;
}
for(int i = 0 ;i < n ;i ++)
{
int x,y;
cin>>x>>y;
cout<<(ll)fact[x] * infact[y] % mod * infact[x - y]%mod<<endl;
}
}