前几天师兄面试碰到一个T厂的算法题,影响是5道算法题,前两题听说很简单,第三题就很难了,下面看一下第三题的内容,具体题目我记不太清楚了,所以我就直接简化题目
本文参考:https://blog.csdn.net/arrowlll/article/details/52629448
输入描述:
求C(n,m)mod p 0<m<n<2000
一、组合定义
阶乘在大二数据结构中就接触过,基本数据类型基本不能满足要求,所以要用一下算法进行简化
二、逆元的定义
对于正整数 a 和 p,如果有 ,那么把这个同余方程中 xx 的最小正整数解叫做 aa 模 pp 的逆元。
举个例子:如果,那么那么x的最小正整数解就是 a 的逆元。可以进一步转换:等同于求
(a*(b的逆元)%p),组合数的除法就可以得到解决。
三、费马小定理
因为在算法竞赛中模数p总是质数,所以可以利用费马小定理 :得到b 的逆元是
四、算法实现
算法实现为参考博客中的实现方法:
- 求取1到n的阶乘对 mod 取模的结果存入数组 JC[] 中;
- 求取 C(n,r) 时, “费马小定理+快速幂”求 JC[r]的逆元存入临时变量 ;
- 然后计算存入临时变量 ;( 即为 的值)
- 求取JC[n - r] 的逆元存入临时变量
- 则可以得到
typedef long long LL;
const LL maxn(1000005), mod(1e9 + 7);
LL Jc[maxn];
void calJc() //求maxn以内的数的阶乘
{
Jc[0] = Jc[1] = 1;
for(LL i = 2; i < maxn; i++)
Jc[i] = Jc[i - 1] * i % mod;
}
//费马小定理求逆元
LL pow(LL a, LL n, LL p) //快速幂 a^n % p
{
LL ans = 1;
while(n)
{
if(n & 1) ans = ans * a % p;
a = a * a % p;
n >>= 1;
}
return ans;
}
LL niYuan(LL a, LL b) //费马小定理求逆元
{
return pow(a, b - 2, b);
}
LL C(LL a, LL b) //计算C(a, b)
{
return Jc[a] * niYuan(Jc[b], mod) % mod
* niYuan(Jc[a - b], mod) % mod;
}