题目传送门

求组合数的时候,如果模数p是质数,可以用卢卡斯定理解决。

但是卢卡斯定理仅仅适用于p是质数的情况。

当p不是质数的时候,我们就需要用扩展卢卡斯求解。

实际上,扩展卢卡斯=快速幂+快速乘+exgcd求逆元+质因数分解+crt合并答案+求阶乘,跟卢卡斯定理没什么关系......

如果把模数p分解成p1^k1*p2^k2*...*px^kx的形式,那么我们可以求出c(n,m)分别模每个pi^ki的结果,再用中国剩余定理合并即可。

每个pi^ki一定是互质的,所以用朴素crt就行。

根据组合数的定义,c(n,m)=(n!) / (m!*(n-m)!) ,所以我们只要能想办法求出阶乘,就能再利用exgcd求出逆元,进而求出组合数。

接下来唯一的问题就是怎么快速求出 x! 取模 pi^ki 的结果。

考虑如下的经典样例(据说来自popoqqq):(19!)%(3^2)

19!=1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19

先把其中的3的倍数提出来,因为求组合数的时候分子分母能约掉。

19!=(1*2*4*5*7*8)*(10*11*13*14*16*17)*(19)*(3*6*9*12*15*18)=(1*2*4*5*7*8)*(1*2*4*5*7*8)*(3*3*3*3*3*3)*(1*2*3*4*5*6)=(1*2*4*5*7*8)^2*19*(3^6)*(1*2*3*4*5*6)。

后面的6!部分可以递归求解,递归终点为0!=1。

3^6最后计算组合数的时候再处理。

那几个(1*2*4*5*7*8)显然是循环的,循环节长度小于pi^ki,可以暴力计算。

显然一共有(x/(pi^ki))个循环节,套个快速幂即可。

剩下的部分,即19,长度等于x%(pi^ki),也小于pi^ki,也可以暴力计算。

至此我们求出了阶乘。

求组合数的时候,考虑pi的倍数的影响。

分子分母分别计数相加减。

最后用crt合并即可。

  1. #include<cstdio>
  2. typedef long long ll;
  3.  
  4. ll n,m,p;
  5.  
  6. ll ksm(ll b,ll tp,ll mod)
  7. {
  8. ll ret=;
  9. while(tp)
  10. {
  11. if(tp&)ret=ret*b%mod;
  12. b=b*b%mod;
  13. tp>>=;
  14. }
  15. return ret;
  16. }
  17.  
  18. ll mul(ll a,ll b,ll mod)
  19. {
  20. ll ret=;
  21. while(b)
  22. {
  23. if(b&)ret=(ret+a)%mod;
  24. a=(a+a)%mod;
  25. b>>=;
  26. }
  27. return ret;
  28. }
  29.  
  30. ll exgcd(ll a,ll b,ll &x,ll &y)
  31. {
  32. if(!b)
  33. {
  34. x=;y=;
  35. return a;
  36. }
  37. ll t=exgcd(b,a%b,y,x);
  38. y-=a/b*x;
  39. }
  40.  
  41. ll inv(ll x,ll mod)
  42. {
  43. ll a,b;
  44. exgcd(x,mod,a,b);
  45. return (a%mod+mod)%mod;
  46. }
  47.  
  48. ll fac(ll x,ll pi,ll pk)
  49. {
  50. if(!x)return ;
  51. ll ans=;
  52. for(ll i=;i<=pk;i++)
  53. if(i%pi)ans=ans*i%pk;
  54. ans=ksm(ans,x/pk,pk);
  55. for(ll i=;i<=x%pk;i++)
  56. if(i%pi)ans=ans*i%pk;
  57. return ans*fac(x/pi,pi,pk)%pk;
  58. }
  59.  
  60. ll c(ll cn,ll cm,ll pi,ll pk)
  61. {
  62. if(cm>cn)return ;
  63. ll up=fac(cn,pi,pk),d1=fac(cm,pi,pk),d2=fac(cn-cm,pi,pk);
  64. ll cnt=;
  65. for(ll i=cn;i;i/=pi)cnt+=i/pi;
  66. for(ll i=cm;i;i/=pi)cnt-=i/pi;
  67. for(ll i=cn-cm;i;i/=pi)cnt-=i/pi;
  68. return up*inv(d1,pk)%pk*inv(d2,pk)%pk*ksm(pi,cnt,pk)%pk;
  69. }
  70.  
  71. ll crt(ll a,ll pk)
  72. {
  73. return a*inv(p/pk,pk)%p*(p/pk)%p;
  74. }
  75.  
  76. int main()
  77. {
  78. scanf("%lld%lld%lld",&n,&m,&p);
  79. ll tp=p,ans=;
  80. for(ll i=;i*i<=p;i++)
  81. {
  82. if(tp%i)continue;
  83. ll pk=;
  84. while(!(tp%i))tp/=i,pk*=i;
  85. ans=(ans+crt(c(n,m,i,pk),pk))%p;
  86. }
  87. if(tp>)ans=(ans+crt(c(n,m,tp,tp),tp))%p;
  88. printf("%lld",(ans%p+p)%p);
  89. return ;
  90. }

[洛谷P4720] [模板] 扩展卢卡斯的更多相关文章

  1. [洛谷P4777] [模板] 扩展中国剩余定理

    扩展中国剩余定理,EXCRT. 题目传送门 重温一下中国剩余定理. 中国剩余定理常被用来解线性同余方程组: x≡a[1] (mod m[1]) x≡a[2] (mod m[2]) ...... x≡a ...

  2. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  3. 洛谷P4720 【模板】扩展卢卡斯

    P4720 [模板]扩展卢卡斯 题目背景 这是一道模板题. 题目描述 求 C(n,m)%P 其中 C 为组合数. 输入输出格式 输入格式: 一行三个整数 n,m,p ,含义由题所述. 输出格式: 一行 ...

  4. 洛谷 P4720 【模板】扩展 / 卢卡斯 模板题

    扩展卢卡斯定理 : https://www.luogu.org/problemnew/show/P4720 卢卡斯定理:https://www.luogu.org/problemnew/show/P3 ...

  5. 洛谷P3375 [模板]KMP字符串匹配

    To 洛谷.3375 KMP字符串匹配 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.如果 ...

  6. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

  7. 【AC自动机】洛谷三道模板题

    [题目链接] https://www.luogu.org/problem/P3808 [题意] 给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过. [题解] 不再介绍基础知识了,就是裸的模 ...

  8. 洛谷-P5357-【模板】AC自动机(二次加强版)

    题目传送门 -------------------------------------- 过年在家无聊补一下这周做的几道AC自动机的模板题 sol:AC自动机,还是要解决跳fail边产生的重复访问,但 ...

  9. 洛谷.1919.[模板]A*B Problem升级版(FFT)

    题目链接:洛谷.BZOJ2179 //将乘数拆成 a0*10^n + a1*10^(n-1) + ... + a_n-1的形式 //可以发现多项式乘法就模拟了竖式乘法 所以用FFT即可 注意处理进位 ...

随机推荐

  1. Println(Object)小贴士

    println public void println(Object x) 打印 Object,然后终止该行.此方法首先调用 String.valueOf(x) 获取打印对象的字符串值,然后的行为如同 ...

  2. offer(背包问题、DP)

    蒜头君很早就想出国,现在他已经考完了所有需要的考试,准备了所有要准备的材料,于是,便需要去申请学校了.要申请国外的任何大学,你都要交纳一定的申请费用,这可是很惊人的.蒜头君没有多少钱,总共只攒了n万元 ...

  3. python使用rsa非对称加密

    1.安装rsa 支持python 2.7 或者 python 3.5 以上版本 使用豆瓣pypi源来安装rsa pip install -i https://pypi.douban.com/simpl ...

  4. 通过OAuth2.0 获取授权访问SF 用户数据

    站长资讯: 创建应用程序 新建应用程序   访问示例(Python+django) 环境准备: index.html 两种方式: 方式一:采用由用户授权,调用者无需知道SF的用户名与密码 方式二:直接 ...

  5. js变量的相关要点

    如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量. JavaScript 变量生命周期在它声明时初始化. 局部变量在函数执行完毕后销毁. 全局变量在页面关闭后销毁.

  6. 函数动态传参,命名空间,gloabal,nonlocal关键字

    一.函数参数->动态传参(形参的第三种) 动态参数分为两种: 1)动态接收位置参数 普通的位置传参: def func(quality_food,junk_food): print('我要吃', ...

  7. Linux从一台linux机器复制文件到另一台linux机器

    1.功能说明 scp 用于将文件/目录从一台linux系统复制到另一台linux系统.传输协议为SSH协议,保证了传输数据的安全性 其格式如下: (1)scp  本地linux系统文件路径   远程用 ...

  8. windows10使用npm安装vue、vue-cli

    从网上下载了一个免费的vue.js前端模板,准备和Django整合出一个项目出来,然后发现前端代码都是.vue文件,已经整合过.html,很容易,感觉这个.vue的前端稍微复杂一些 本文主要参考博客及 ...

  9. drf框架与postman初始

    drf框架 全称:django-rest framework 知识点 """ 1.接口:什么是接口.restful接口规范 2.CBV生命周期源码 - 基于restful ...

  10. ionic3 发布订阅者模式实现

    在ionic3 中实现订阅发布模式,需要用到Events. Events下面有三个方法 events.subscribe()  订阅 events.publish()  发布 events.unsub ...