1.当n,m都很小的时候可以利用杨辉三角直接求。
C(n,m)=C(n-1,m)+C(n-1,m-1);

2、n和m较大,但是p为素数的时候

Lucas定理是用来求 c(n,m) mod p,p为素数的值

C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p

也就是Lucas(n,m)%p=Lucas(n/p,m/p)*C(n%p,m%p)%p

求上式的时候,Lucas递归出口为m=0时返回1

求C(n%p, m%p)%p的时候,此处写成C(n, m)%p(p是素数,n和m均小于p)

C(n, m)%p = n! / (m ! * (n - m )!) % p = n! * mod_inverse[m! * (n - m)!, p] % p

由于p是素数,有费马小定理可知,m! * (n - m)! 关于p的逆元就是m! * (n - m)!的p-2次方。

p较小的时候预处理出1-p内所有阶乘%p的值,然后用快速幂求出逆元,就可以求出解。p较大的时候只能逐项求出分母和分子模上p的值,然后通过快速幂求逆元求解。

P较大,不打表:

  1. ll pow(ll a, ll b, ll m)
  2. {
  3. ll ans = ;
  4. a %= m;
  5. while(b)
  6. {
  7. if(b & )ans = (ans % m) * (a % m) % m;
  8. b /= ;
  9. a = (a % m) * (a % m) % m;
  10. }
  11. ans %= m;
  12. return ans;
  13. }
  14. ll inv(ll x, ll p)//x关于p的逆元,p为素数
  15. {
  16. return pow(x, p - , p);
  17. }
  18. ll C(ll n, ll m, ll p)//组合数C(n, m) % p
  19. {
  20. if(m > n)return ;
  21. ll up = , down = ;//分子分母;
  22. for(int i = n - m + ; i <= n; i++)up = up * i % p;
  23. for(int i = ; i <= m; i++)down = down * i % p;
  24. return up * inv(down, p) % p;
  25. }
  26. ll Lucas(ll n, ll m, ll p)
  27. {
  28. if(m == )return ;
  29. return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
  30. }

P较小,打表:

  1. const int maxn = 1e5 + ;
  2. ll fac[maxn];//阶乘打表
  3. void init(ll p)//此处的p应该小于1e5,这样Lucas定理才适用
  4. {
  5. fac[] = ;
  6. for(int i = ; i <= p; i++)
  7. fac[i] = fac[i - ] * i % p;
  8. }
  9. ll pow(ll a, ll b, ll m)
  10. {
  11. ll ans = ;
  12. a %= m;
  13. while(b)
  14. {
  15. if(b & )ans = (ans % m) * (a % m) % m;
  16. b /= ;
  17. a = (a % m) * (a % m) % m;
  18. }
  19. ans %= m;
  20. return ans;
  21. }
  22. ll inv(ll x, ll p)//x关于p的逆元,p为素数
  23. {
  24. return pow(x, p - , p);
  25. }
  26. ll C(ll n, ll m, ll p)//组合数C(n, m) % p
  27. {
  28. if(m > n)return ;
  29. return fac[n] * inv(fac[m] * fac[n - m], p) % p;
  30. }
  31. ll Lucas(ll n, ll m, ll p)
  32. {
  33. if(m == )return ;
  34. return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
  35. }

3、n,m较大且p不为素数的时候

扩展Lucas定理:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int maxn = 1e6 + ;
  5. const int mod = 1e9 + ;
  6. ll pow(ll a, ll b, ll m)
  7. {
  8. ll ans = ;
  9. a %= m;
  10. while(b)
  11. {
  12. if(b & )ans = (ans % m) * (a % m) % m;
  13. b /= ;
  14. a = (a % m) * (a % m) % m;
  15. }
  16. ans %= m;
  17. return ans;
  18. }
  19. ll extgcd(ll a, ll b, ll& x, ll& y)
  20. //求解ax+by=gcd(a, b)
  21. //返回值为gcd(a, b)
  22. {
  23. ll d = a;
  24. if(b)
  25. {
  26. d = extgcd(b, a % b, y, x);
  27. y -= (a / b) * x;
  28. }
  29. else x = , y = ;
  30. return d;
  31. }
  32. ll mod_inverse(ll a, ll m)
  33. //求解a关于模上m的逆元
  34. //返回-1表示逆元不存在
  35. {
  36. ll x, y;
  37. ll d = extgcd(a, m, x, y);
  38. return d == ? (m + x % m) % m : -;
  39. }
  40.  
  41. ll Mul(ll n, ll pi, ll pk)//计算n! mod pk的部分值 pk为pi的ki次方
  42. //算出的答案不包括pi的幂的那一部分
  43. {
  44. if(!n)return ;
  45. ll ans = ;
  46. if(n / pk)
  47. {
  48. for(ll i = ; i <= pk; i++) //求出循环节乘积
  49. if(i % pi)ans = ans * i % pk;
  50. ans = pow(ans, n / pk, pk); //循环节次数为n / pk
  51. }
  52. for(ll i = ; i <= n % pk; i++)
  53. if(i % pi)ans = ans * i % pk;
  54. return ans * Mul(n / pi, pi, pk) % pk;//递归求解
  55. }
  56.  
  57. ll C(ll n, ll m, ll p, ll pi, ll pk)//计算组合数C(n, m) mod pk的值 pk为pi的ki次方
  58. {
  59. if(m > n)return ;
  60. ll a = Mul(n, pi, pk), b = Mul(m, pi, pk), c = Mul(n - m, pi, pk);
  61. ll k = , ans;//k为pi的幂值
  62. for(ll i = n; i; i /= pi)k += i / pi;
  63. for(ll i = m; i; i /= pi)k -= i / pi;
  64. for(ll i = n - m; i; i /= pi)k -= i / pi;
  65. ans = a * mod_inverse(b, pk) % pk * mod_inverse(c, pk) % pk * pow(pi, k, pk) % pk;//ans就是n! mod pk的值
  66. ans = ans * (p / pk) % p * mod_inverse(p / pk, pk) % p;//此时用剩余定理合并解
  67. return ans;
  68. }
  69.  
  70. ll Lucas(ll n, ll m, ll p)
  71. {
  72. ll x = p;
  73. ll ans = ;
  74. for(ll i = ; i <= p; i++)
  75. {
  76. if(x % i == )
  77. {
  78. ll pk = ;
  79. while(x % i == )pk *= i, x /= i;
  80. ans = (ans + C(n, m, p, i, pk)) % p;
  81. }
  82. }
  83. return ans;
  84. }
  85.  
  86. int main()
  87. {
  88. ll n, m, p;
  89. while(cin >> n >> m >> p)
  90. {
  91. cout<<Lucas(n, m, p)<<endl;
  92. }
  93. return ;
  94. }

组合数取模方法总结(Lucas定理介绍)的更多相关文章

  1. 2015 ICL, Finals, Div. 1 Ceizenpok’s formula(组合数取模,扩展lucas定理)

    J. Ceizenpok’s formula time limit per test 2 seconds memory limit per test 256 megabytes input stand ...

  2. 组合数取模介绍----Lucas定理介绍

    转载https://www.cnblogs.com/fzl194/p/9095177.html 组合数取模方法总结(Lucas定理介绍) 1.当n,m都很小的时候可以利用杨辉三角直接求. C(n,m) ...

  3. CF451E Devu and Flowers (隔板法 容斥原理 Lucas定理 求逆元)

    Codeforces Round #258 (Div. 2) Devu and Flowers E. Devu and Flowers time limit per test 4 seconds me ...

  4. 【Gym 100947E】Qwerty78 Trip(组合数取模/费马小定理)

    从(1,1)到(n,m),每次向右或向下走一步,,不能经过(x,y),求走的方案数取模.可以经过(x,y)则相当于m+n步里面选n步必须向下走,方案数为 C((m−1)+(n−1),n−1) 再考虑其 ...

  5. Codeforces 57C (1-n递增方案数,组合数取模,lucas)

    这个题相当于求从1-n的递增方案数,为C(2*n-1,n); 取模要用lucas定理,附上代码: #include<bits/stdc++.h> using namespace std; ...

  6. [UOJ 275/BZOJ4737] 【清华集训2016】组合数问题 (LUCAS定理的运用+数位DP)

    题面 传送门:UOJ Solution 这题的数位DP好蛋疼啊qwq 好吧,我们说回正题. 首先,我们先回忆一下LUCAS定理: \(C_n^m \equiv C_{n/p}^{m/p} \times ...

  7. BZOJ 3782: 上学路线 [Lucas定理 DP]

    3782: 上学路线 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 192  Solved: 75[Submit][Status][Discuss] ...

  8. [BZOJ4591][SHOI2015]超能粒子炮·改(Lucas定理+数位DP)

    大组合数取模可以想到Lucas,考虑Lucas的意义,实际上是把数看成P进制计算. 于是问题变成求1~k的所有2333进制数上每一位数的组合数之积. 数位DP,f[i][0/1]表示从高到低第i位,这 ...

  9. 组合数取模&&Lucas定理题集

    题集链接: https://cn.vjudge.net/contest/231988 解题之前请先了解组合数取模和Lucas定理 A : FZU-2020  输出组合数C(n, m) mod p (1 ...

随机推荐

  1. iOS开源项目周报0323

    由OpenDigg 出品的iOS开源项目周报第十三期来啦.我们的iOS开源周报集合了OpenDigg一周来新收录的优质的iOS开源项目,方便iOS开发人员便捷的找到自己需要的项目工具等. CHIPag ...

  2. C# 视频转换类

    using System.Web; using System.Configuration; namespace DotNet.Utilities { public class VideoConvert ...

  3. 使用http维持socket长连接

    项目中有遇到问题如下: 1.旧版的cs服务,因为每个用户和唯一的长连接是在登录后绑定的,并且所有的消息报文均是基于该长连接去发送接收的,所以要求node服务要维持一个长连接,然后根据该用户获取长连接, ...

  4. git merge后,后悔了如何回退

    今天将feature分支的代码merge到develop分支后我后悔了,因为feature分支的功能还没有全部开发完成,我在feature分支上commit是可以的,但是这之后我又把它merge到了d ...

  5. [linux] C语言Linux系统编程-socket回声客户端

    回声客户端: 1.所谓“回声”,是指客户端向服务器发送一条数据,服务器再将数据原样返回给客户端,就像声音一样,遇到障碍物会被“反弹回来”. 2.客户端也可以使用 write() / send() 函数 ...

  6. [javaSE] GUI(jar包双击运行)

    首先应该在java文件中定义包名,package 包名 带包编译成class文件 切换到目录下,使用jar -cvf xx.jar 包名,就是把那个包放到xx.jar包里面 此时双击会报错,找不到要执 ...

  7. oracle sum(col1) over(partition by col2 order by col3):实现分组递增汇总

    应公司业务要求,需要对数据进行分组汇总做辅助列进行查询 所以使用到了sum(col1) over(partition by col2 order by col3)函数,为了学习与提高在此进行记录. 1 ...

  8. OpenStack IceHouse 部署 - 4 - 计算节点部署

    Nova计算服务(计算节点)  参考 本页内容依照官方安装文档进行,具体参见Configure a compute node(nova service) 前置工作 数据库 由于我们在Nova(计算管理 ...

  9. Django实现数据库中表格的增删查改

    1.urls.py """Django_demo1 URL Configuration The `urlpatterns` list routes URLs to vie ...

  10. 牛客Wannafly挑战赛23F 计数(循环卷积+拉格朗日插值/单位根反演)

    传送门 直接的想法就是设 \(x^k\) 为边权,矩阵树定理一波后取出 \(x^{nk}\) 的系数即可 也就是求出模 \(x^k\) 意义下的循环卷积的常数项 考虑插值出最后多项式,类比 \(DFT ...