其实我并不是要写排列组合什么的,我是来说说当n很大时,我们怎么快速处理出排列组合。
第一种想法,直接写,long long或者double,但是1000都上不去,那怎么办?
第二种想法,我们知道排列组合公式中,出现了阶乘,也就是“!”,对,从这里我们可以挖掘出一些性质。
性质1:n!标准分解式中质因数p的指数h = [n/p] + [n/p^2] + ...
性质1简直帮了大忙,这样,我们可以得到n的所有质因子的指数,而当n为10^10时,它的最大质数小于10^5,个数就更少了。而且在排列组合中,分母处减,分子出加,这样就得到了排列组合的所有质因子的指数。
void up(int k)//求k!的质因子指数
{
int mul, temp;
FOR(i, 1, prime_num)//枚举质数
{
mul = prime[i];
if(mul > k) return;
temp = mul;
while(temp <= k)
{
cnt[i] += k / temp;
temp *= mul;
}
}
}
void down(int k)
{
int mul, temp;
FOR(i, 1, prime_num)
{
mul = prime[i];
if(mul > k) return;
temp = mul;
while(temp <= k)
{
cnt[i] -=k / temp;
temp *= mul;
}
}
}
找完后,我们就要做一下高精度了,这个还是很简单的,就不给出代码了。说一个小优化,当质数p的质数很大时,我们可以每次高精度乘时乘上p^2或p^3...只要不要太大而溢出就可以了,速度还是很快的。
以上为处理的一种方法,适用于各种情况,那么还有没有其他更简单的方法呢?对,还有一种方法,用到了乘法逆元。你找到某个数的a关于模b的乘法逆元x后,除a就可以等价于乘x了,这样就可以把所有排列组合里面里出现的除法变成乘法了,还是很方便的。
当然,这种方法有一个限制条件就是必须要存在乘法逆元才可以。在扩展欧几里得部分我已经给出,ax≡1(mod b)存在乘法逆元的条件是:gcd(a,b)=1,因此在这种情况下,我们可以使用乘法逆元。求法有两种,一种在扩展欧几里得部分中给出了,另一种是用欧拉定理求乘法逆元,下面讲解一下。
我们要得到ax≡1(mod b)的形式(其中a,b互质),由欧拉定理得aΦ(b)≡1(mod b),转变一下有a*aΦ(b)-1≡1(mod b),因此aΦ(b)-1为乘法逆元。此时我们就得到了乘法逆元。通过快速幂求解一下就好了。注意的是,通常b为质数,那么我们就有Φ(b)=b-1,那么题目就更简单了。
得到乘法逆元后,我们最后通过一系列的乘法就可以快速求得排列组合了。
浙公网安备 33010602011771号