快速求排列组合C(m,n)%mod

写在前面:
1. 为防止产生n和m的歧义,本博文一律默认n >= m
2. 本博文默认mod = 10^6+3
3. 本博文假设读者已知排列组合公式

C(m,n)=n!(n−m)!∗m!

4. 普通的小数据就不用多说了,直接用公式,当然别忘了取模

C(m,n)=C(m−1,n−1)+C(m,n−1)

现在我们讨论当n可达10^9数量级大小时的算法。

步骤一:我们先把分子阶乘写成以下形式

n!=X∗modY

步骤二:对分母元素乘机求逆元。此时我们假设得到了以下方程式:

n!(n−m)!∗m!=A∗modBGamma(T)∗modD=A∗C∗modBmodD

其中Gamma(T)表示分母剩余数字的乘积,C为他的逆元。

步骤三:显然根据上式我们就可以得出结论了

  • 如果B > D ,那么我们的最终答案为 C(m,n)% mod = 0
  • 否则我们的答案为C(m,n) = (A * C) % mod

注意事项

  • 求A的过程中,我们会发现最后的结果会变成:

    [1∗2∗...∗(mod−1)∗(mod+1)∗…]∗modB∗[1∗2∗…∗(n/mod)]
  • 如果有多组数据,而mod的大小又不变,那么我们完全可以对k!%mod进行预处理

  • 在对分母的每个元素求逆元时,我们可以由mod是素数直接用欧拉函数求出其逆元

    1. 欧拉函数

      phi(mod)=mod−1
    2. 求逆元,用快速幂,同时别忘了取模
      inv(x)=xphi(mod)−1

最后代码如下:

@Frosero
#include <iostream>
#include <algorithm>
using namespace std; const long long mod = 1000003;
long long mul[1000100]; long long pow_mod(long long a ,long long p){ //递归快速幂
if(p == 0) return 1;
long long ans = pow_mod(a,p/2);
ans = ans * ans % mod;
if(p % 2) ans = ans * a % mod;
return ans;
} void ask(long long n,long long &x,long long y){ //将阶乘 n! 拆分成 x * mod ^ y 的形式
y = n / mod;
x = (pow_mod(mul[mod - 1],y) * mul[n - y * mod] * mul[y]) % mod;
} int main(){
mul[0] = mul[1] = 1;
for(long long i = 2 ;i < mod ;i++){ //预处理 0 至 mod-1 的阶乘取模值
mul[i] = mul[i - 1] * i % mod;
} long long m,n,x,y;
long long A,B,C,D;
while(cin >> m >> n){ //对应上文中所讲的 即转换成 A * C * mod ^ B / mod ^ D 的形式
ask(n,A,B); ask(m,C,D); ask(n-m,x,y);
D += y;
C = pow_mod(C,mod - 2) * pow_mod(x,mod - 2) % mod;
if(B > D) cout<<0<<endl;
else cout<<A * C % mod<<endl;
}
return 0;
}

补充

  1. 由以上可知算法时间复杂度和mod的取值大小有关
  2. 如果n的大小超过mod * mod 时要考虑特殊情况,读者可以自己想
  3. 如果mod不是素数时,我们可以换一种方法。希望大家自己思考,这里就不啰嗦了

快速求排列C(m,n)加取模的更多相关文章

  1. 快速求排列组合 lucas定理

    对于C(n, m) mod p.这里的n,m,p(p为素数)都很大的情况. 就不能再用C(n, m) = C(n - 1,m) + C(n - 1, m - 1)的公式递推了. 一般lucas定理的p ...

  2. 牛客Wannafly挑战赛13-BJxc军训-费马小定理、分式取模、快速幂

    参考:https://blog.csdn.net/qq_40513946/article/details/79839320 传送门:https://www.nowcoder.com/acm/conte ...

  3. [hdu5392 Infoplane in Tina Town]置换的最小循环长度,最小公倍数取模,输入挂

    题意:给一个置换,求最小循环长度对p取模的结果 思路:一个置换可以写成若干循环的乘积,最小循环长度为每个循环长度的最小公倍数.求最小公倍数对p取模的结果可以对每个数因式分解,将最小公倍数表示成质数幂的 ...

  4. 牛客小白月赛9 A签到(分数取模,逆元)

    传送门 对分母求一下逆元,把除法取模变成乘法取模,逆元介绍看这里 这种方法只适合模为质数的情况 #include<bits/stdc++.h> using namespace std; ; ...

  5. np问题(大数阶乘取模)

    转自 np问题 题目描述: LYK 喜欢研究一些比较困难的问题,比如 np 问题. 这次它又遇到一个棘手的 np 问题.问题是这个样子的:有两个数 n 和 p,求 n 的阶乘对 p 取模后的结果. L ...

  6. The 2018 ACM-ICPC China JiangSu Provincial Programming Contest快速幂取模及求逆元

    题目来源 The 2018 ACM-ICPC China JiangSu Provincial Programming Contest 35.4% 1000ms 65536K Persona5 Per ...

  7. 九度OJ 1085 求root(N, k) -- 二分求幂及快速幂取模

    题目地址:http://ac.jobdu.com/problem.php?pid=1085 题目描述: N<k时,root(N,k) = N,否则,root(N,k) = root(N',k). ...

  8. NYOJ--102--次方求模(快速求幂取模)

    次方求模 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 求a的b次方对c取余的值   输入 第一行输入一个整数n表示测试数据的组数(n<100)每组测试只有一 ...

  9. 二分求幂/快速幂取模运算——root(N,k)

    二分求幂 int getMi(int a,int b) { ; ) { //当二进制位k位为1时,需要累乘a的2^k次方,然后用ans保存 == ) { ans *= a; } a *= a; b / ...

随机推荐

  1. 【硬盘】RAID

    RAID是英文Redundant Array of Independent Disks(独立磁盘冗余阵列),简称磁盘阵列.下面将各个级别的RAID介绍如下. 一.为什么使用Raid? 1.对磁盘高速存 ...

  2. UNP学习 非阻塞I/O

    缺省状态下,套接口时阻塞方式的.这意味着当一个套接口调用不能立即完成时,进程进入睡眠状态,等待操作完成.我们将可能阻塞的套接口调用分成四种. 1.输入操作:read.readv.recv.recvfr ...

  3. 芒果学院Python高级开发课程

  4. C#排列组合类,写彩票算法的朋友们可以来看一看

    public class PermutationAndCombination<T>    {        /// <summary>        /// 交换两个变量    ...

  5. POJ 1151 线段树+扫描线(计算矩形面积并)

    前一篇博客有了讲解就不再叙述了 #include<cstdio> #include<cstring> #include<cmath> #include<ios ...

  6. Java Software Engineer Skill Map

    # Java Software Engineer Skill Map## Basic### Core Java- Java The Complete Reference Ninth Edition.p ...

  7. Canal( 增量数据订阅与消费 )的理解及应用

    canal是阿里巴巴旗下的一款开源项目,纯Java开发.基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL(也支持mariaDB). 起源:早期,阿里巴巴B2B公司因为存 ...

  8. PHP生成PDF完美支持中文,解决TCPDF乱码

    PHP生成PDF完美支持中文,解决TCPDF乱码 2011-09-26 09:04 418人阅读 评论(0) 收藏 举报 phpfontsheaderttfxhtml文档 PHP生成PDF完美支持中文 ...

  9. Linux环境下Eclipse对C++新特性的支持设置

    Linux环境下Eclipse对C++新特性的支持设置     今天写一个简单的关于C11中的array容器的测试程序如下, #include <iostream> #include &l ...

  10. Could not open lock file/var/lib/dpkg/lock的解决

    Could not open lock file/var/lib/dpkg/lock的解决 在ubuntu系统中利用apt-get install something的时候,有时候会出现无法获得锁的权 ...