组合数的计算方法
全文共 117 字预计阅读 1 分钟
#计算组合数的几种方法
前置知识 费马小定理(求乘法逆元) 若为逆元
可以得到
第一种 暴力
根据定义
在 的时候可以直接用 暴力循环求。
long long C(int a, int b)
{
long long res = 1;
for (int i=1,j=a;i<=b;i++,j--)
{
res = res * j / i;
}
return res;
}第二种 递推公式
给定 组询问,每组询问给定两个整数 ,,请你输出 的值。
数据范围
不难发现, 和 的范围不大,所有的组合有 共 种,将所有情况预处理出来,对每次查询,可以在规定时间内通过。 利用递推公式:
for (int i=0;i<N;i++)
{
for (int j=0;j<=i;j++)
{
if (j == 0) c[i][j] = 1;
else c[i][j] = c[i-1][j] + c[i-1][j-1];
}
}第三种 快速幂
给定 组询问,每组询问给定两个整数 ,,请你输出 的值。
数据范围
相比于第一种,第二种的 与 扩大了范围。这时可以用组合数的定义式直接求,但要用到快速幂算法求逆元。
在计算阶乘的时候,可以进行预处理,阶乘的逆元同理。
LL quickPower(LL a,LL b,LL m)
{
LL res = 1;
while (b)
{
if (b & 1) res *= a;
res %= m;
b >>= 1;
a *= a;
a %= m;
}
return res;
}
fact[0] = infact[0] = 1;
for (int i=1;i<N;i++)
{
fact[i] = fact[i-1] * i % mod;
infact[i] = infact[i-1] * quickPower(i, mod-2, mod) % mod;
}逆元阶乘可以直接相乘,是不是说 的逆元乘上 的逆元就是 的逆元?
是的,如果 是 的逆元, 是 的逆元,那么 ,所以 ,所以 是 的逆元。
第四种 定理
给定 组询问,每组询问给定三个整数 ,其中 是质数,请你输出 的值。
数据范围
在这种情况,测试数据组数比较小,但是 和 的范围很大,这时要用到 定理。
LL qmi(LL a, LL k) //快速幂
{
LL res = 1;
while (k)
{
if (k & 1) res *= a;
res %= p;
k >>= 1;
a *= a;
a %= p;
}
return res;
}
LL C(LL a,LL b) //定义求组合数
{
LL res = 1;
for (LL i=1, j=a;i<=b;i++,j--)
{
res = res * j % p;
res = res * qmi(i, p-2) % p;
}
return res;
}
LL lucas(LL a, LL b) //Lucas定理
{
if (a < p) return C(a,b);
return C(a % p, b % p) * lucas(a / p, b / p) % p;
}