携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第45天,点击查看活动详情
AcWing 886. 求组合数 II
给定 n 组询问,每组询问给定两个整数 a,b,请你输出 Ca^b mod (10^9+7) 的值。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一组 a 和 b。
输出格式
共 n 行,每行输出一个询问的解。
数据范围
1≤n≤10000,
1≤b≤a≤10^5
输入样例:
3
3 1
5 3
2 2
输出样例:
3
10
1
思路
若在mod p意义下,对于一个整数a,有a*b≡1(mod p),那么这个整数b即为a的乘法逆元,同时a也为b的乘法逆元,这里的 b 对于 a 来说类似于 1/a 的存在。
求 (a / b)%p 等同于求 a∗(b的逆元)%p,除以一个数等同于乘一个数的倒数。
有什么用呢?对于一个分数,分子分母同余一个数是不等于原分数模这个数的,而两个数相乘的时候是可以的。
即 (a / b) % p ≠ ((a % p ) / ( b % p)) % p,而 (a * b) % p = ((a % p ) * ( b % p)) % p
需要取模运算的时候,乘法是优于除法的,所以有了逆元的概念。
通过预处理逆元的方式求组合数模板
首先预处理出所有阶乘取模的余数fact[N],以及所有阶乘取模的逆元infact[N]
如果取模的数是质数,可以用费马小定理求逆元
int qmi(int a, int k, int p){ // 快速幂模板
int res = 1;
while (k){
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
// 预处理阶乘的余数和阶乘逆元的余数
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;
}
ac代码
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010, mod = 1e9 + 7;
int fact[N], infact[N];
int qmi(int a, int k, int p){
int res = 1;
while (k){
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int main(){
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;
}
int n;
scanf("%d", &n);
while (n -- ){
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", (LL)fact[a] * infact[b] % mod * infact[a - b] % mod);
}
return 0;
}