72.Acwing基础课第886题-简单-求组合数Ⅱ
72.Acwing基础课第886题-简单-求组合数Ⅱ
题目描述
\(给定 n 组询问,每组询问给定两个整数 a,b,请你输出 C^b_amod(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
代码:
// 包含基础输入输出、算法头文件
#include <iostream>
#include <algorithm>
using namespace std;
// 定义长整型别名:防止乘法过程中溢出int范围(如1e5!的中间结果极大)
typedef long long LL;
// 常量定义:
// N=100010:预处理阶乘/逆元的最大范围(支持C(a,b)中a≤1e5)
// mod=1e9+7:模数(质数,适配费马小定理求逆元)
const int N = 100010, mod = 1e9 + 7;
// 预处理数组:
// fact[i] = i! mod mod(i的阶乘模mod)
// infact[i] = (i!)^{-1} mod mod(i!的乘法逆元模mod)
int fact[N], infact[N];
// 快速幂函数:计算 (a^k) mod p 的结果(费马小定理求逆元的核心)
// 参数:a-底数,k-指数,p-模数(本题中p=mod是质数)
int qmi(int a, int k, int p)
{
int res = 1; // 初始化结果为1(乘法单位元)
// 二进制分解指数k,时间复杂度O(logk)
while (k)
{
// k&1:判断k的二进制最低位是否为1(等价于k%2==1)
if (k & 1) res = (LL)res * a % p; // 结果乘当前底数,转LL防溢出
a = (LL)a * a % p; // 底数平方,转LL防溢出
k >>= 1; // 指数右移一位(等价于k /= 2)
}
return res; // 返回 (a^k) mod p
}
int main()
{
// ========== 预处理阶乘和阶乘的逆元 ==========
// 边界条件:0! = 1,0!的逆元也为1
fact[0] = infact[0] = 1;
// 遍历1到N-1,预处理每个数的阶乘和阶乘逆元
for (int i = 1; i < N; i ++ )
{
// 阶乘递推:i! = (i-1)! * i mod mod
fact[i] = (LL)fact[i - 1] * i % mod;
// 阶乘逆元递推:(i!)^{-1} = (i-1)!^{-1} * i^{-1} mod mod
// 其中i^{-1}(i的逆元)由费马小定理得:i^(mod-2) mod mod(mod是质数)
infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}
// ========== 处理多组查询 ==========
int n; // n:查询的组数
scanf("%d", &n);
while (n -- )
{
int a, b; // a:总数;b:选取数(查询C(a,b))
scanf("%d%d", &a, &b);
// 组合数公式:C(a,b) = a!/(b!*(a-b)!) mod mod
// 模运算中除法等价于乘逆元:C(a,b) = (a! * (b!)^{-1} * (a-b)!^{-1}) mod mod
// 分步取模:避免中间结果溢出,保证每一步都在mod范围内
printf("%d\n", (LL)fact[a] * infact[b] % mod * infact[a - b] % mod);
}
return 0;
}

浙公网安备 33010602011771号