zoukankan      html  css  js  c++  java
  • 拓展Lucas小结

    拓展Lucas是解决大组合数取模非质数(尤其是含平方因子的合数)问题的有力工具...

    首先对模数质因数分解,把每个质因子单独拎出来处理答案,然后用中国剩余定理(excrt)合并

    问题转化为,对于每个质因子p,求$C_{n}^{m}(mod;p^k)$

    把$C_{n}^{m}$展开成$frac{n!}{m!(n-m)!}$,发现上下的阶乘里,都可能有质因子p,把它们从阶乘里提取出来,额外求出$n!$里p的数量,减掉$m!$和$(n-m)!$里p的数量,再乘回答案里

    剩余的部分就是$n!$,$m!$和$(n-m)!$去掉p的部分,因为它们都关于模数$p^k$互质,除以$m!$和$(n-m)!$就可以用exgcd求逆元了

    现在剩余的任务就是处理$n!$去掉所有p的剩余部分

    比如n=19,p=3,k=2时

    剩余$1*2*4*5*7*8*10*11*13*14*16*17*19(mod;3^2)$

    略微变形成$(1*2*4*5*7*8)*(10*11*13*14*16*17)*19(mod;3^2)$

    发现它竟然是一个又一个循环节,循环节长度为$p^k$,而且每一个循环节都是同模的,所以计算出一个循环节的答案,再用快速幂求得所有循环节相乘的答案

    对于每个循环节,递归求解即可

    每次计算还会剩余长度小于循环节一小段,因为它长度小于$p^k$,暴力计算就行了

    最后合并每个质因子的答案即可模数

    时间非常玄学,由出题人的毒瘤程度模数决定

    代码好长啊

     1 namespace exlucas{
     2 ll ans=0,M=1;
     3 ll son[10],pw[10];
     4 int num;
     5 int excrt_ins(ll A,ll B)
     6 {
     7     ll a=A,b=B,c=(a-ans%b+b)%b,x,y;
     8     ll g=exgcd(M,b,x,y);ll bg=b/g;
     9     if(c%g!=0) return -1;
    10     //x=x*(c/g)%bg;
    11     x=qmul(x,c/g,bg);
    12     ans+=x*M,M*=bg,ans=(ans%M+M)%M;
    13     return 1;
    14 }
    15 ll get_mul(ll n,ll p,ll &sum,const ll &mo,int type)
    16 {
    17     if(n==0) return 1;
    18     ll ans=1;
    19     for(int i=2;i<=min(n,mo);i++)
    20         if(i%p) ans=ans*i%mo;
    21     ans=qpow(ans,n/mo,mo);
    22     for(int i=2;i<=n%mo;i++)
    23         if(i%p) ans=ans*i%mo;
    24     sum+=1ll*(n/p)*type;
    25     return ans*get_mul(n/p,p,sum,mo,type)%mo;
    26 }
    27 ll get_C(ll n,ll m,ll p,const ll &mo)
    28 {
    29     if(m>n) return 0;
    30     ll sum=0;ll y;
    31     ll nn=get_mul(n,p,sum,mo,1);
    32     ll mm=get_mul(m,p,sum,mo,-1);
    33     ll nm=get_mul(n-m,p,sum,mo,-1);
    34     exgcd(mm,mo,mm,y);
    35     mm=(mm%mo+mo)%mo;
    36     exgcd(nm,mo,nm,y);
    37     nm=(nm%mo+mo)%mo;
    38     return nn*mm%mo*nm%mo*qpow(p,sum,mo)%mo;
    39 }
    40 ll C(ll n,ll m,const ll &mo)
    41 {
    42     if(m>n) return 0;
    43     ll ret=0;
    44     for(int i=0;i<num;i++){
    45         ll val=get_C(n,m,son[i],pw[i]);
    46         excrt_ins(val,pw[i]);
    47     }
    48     ret=ans,M=1,ans=0;
    49     return ret;
    50 }
    51 };
  • 相关阅读:
    Linux-文件目录管理
    20. 有效的括号
    242. 有效的字母异位词
    387. 字符串中的第一个唯一字符
    136. 只出现一次的数字
    14. 最长公共前缀
    268. 丢失的数字
    169. 多数元素
    26. 删除有序数组中的重复项
    283. 移动零
  • 原文地址:https://www.cnblogs.com/guapisolo/p/9891409.html
Copyright © 2011-2022 走看看