• 组合数取模学习笔记


    组合数取模的话,之前多少会一些,能应付一般的题目,而这次遇到了模数为合数的题目,于是就又来学习了一发.
    这次看到了一个比较不错的blog:https://blog.csdn.net/skywalkert/article/details/52553048
    在这个blog里,其1.3里的内容,有许多不理解的地方,并且3.2及以后的内容,并没有去研究.
    这次主要是get到了用crt解决模数为合数的问题,并且还有与其配套使用的模数为质数的幂的问题.
    复习了一下crt,crt就是去按照一个正确且比较方便的方法去构造一个解,并且利用了数在模里模外意义不同.
    下面给出解决此类问题的代码以及代码注释:
    (此代码对应于具体题目,请读者抓住重点)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    typedef long long LL;
    const int N=1000010;
    int n,x,y,mod,P,p,phi,fac[N],num;
    inline int Pow(int x,int y){
      int ret=1;
      while(y){
        if(y&1)ret=(LL)ret*x%P;
        x=(LL)x*x%P,y>>=1;
      }
      return ret;
    }
    inline int cnt(int n){
      return n?n/p+cnt(n/p):0;
      //递归处理n的阶乘里p的个数
    }
    inline int sum(int n){
      return n?((LL)(n/P?Pow(fac[P],n/P):1)*fac[n%P]%P*sum(n/p)%P):1;
      //递归处理n的阶乘里刨去p之后,在模P的意义下的结果
      //为什么要递归呢?
      //我们发现,我们预处理的时候,如果预处理的是刨去p的,那么他根本不循环.
      //因为对于一个含有p的数来说,他刨去p,和他加上P之后再刨去p,是不一样的.
      //所以说,我们要预处理的是不含p的数的阶乘,也就是说,如果一个数含有p,那就不乘他.
      //那么我们首先处理的是不含p的,然后去递归含有一个p的,然后是两个的……
      //这样答案就对了.
      //这样好骚啊,一开始由于不知道这个操作而错*N……
      //不过这玩意log^2的吧……
    }
    //以上两个函数每次都尼玛可以在一开始处理阶乘的时候处理出来……然后就会跑得飞快……
    //不过预处理的话,必须要求n较小,但是递归的话,只要n或P中的一个很小就可以了.
    #define deal(n,a,b) int a=sum(n),b=cnt(n);
    inline int C(int n,int m){
      if(m>n)return 0;
      deal(n,a0,b0)
      deal(m,a1,b1)
      deal(n-m,a2,b2)
      b0-=b1+b2;
      if(b0>=num)return 0;
      return (LL)a0*Pow(a1,phi-1)%P*Pow(a2,phi-1)%P*Pow(p,b0)%P;
    }
    inline int calc(){
      int ret=0,i,a,b,c,d;
      fac[0]=1;
      for(i=1;i<=P&&i<=n;++i)
        fac[i]=(i%p)?(LL)fac[i-1]*i%P:fac[i-1];
      a=0,b=x,c=(n-y-x)>>1,d=(n+y-x)>>1;
      while(c>=0){
        ret=(ret+(LL)C(n,a+b)*C(a+b,a)%P*C(c+d,c)%P)%P;
        ++a,++b,--c,--d;
      }
      return ret;
    }
    int main(){
      //freopen("rio.in","r",stdin);
      scanf("%d%d%d%d",&n,&mod,&x,&y);
      x=std::abs(x),y=std::abs(y);
      if(n-y-x<0||((x&1)!=((n+y)&1))){
        puts("0");
        return 0;
      }
      int i,s=mod,ans=0;
      for(i=2;s>1;++i){
        if(i*i>s)i=s;
        if(s%i==0){
          p=i,P=1,num=0;
          while(s%p==0)
            ++num,P*=p,s/=p;
          phi=P/p*(p-1);
          ans=(ans+(LL)calc()*(mod/P)%mod*Pow(mod/P,phi-1))%mod;
        }
      }
      printf("%d
    ",ans);
      return 0;
    }
  • 相关阅读:
    nginx(二)----ubuntu14.04下启动或重启和关闭nginx
    nginx(一)----ubuntu14.04下安装nginx
    ubuntu安装和查看已安装
    《太阳照常升起》
    [原]Jenkins(十四)---jenkins示例:admin管理所有项目,新建用户只能看部分项目
    第十二节:Nginx的简介、三种轮询配置方式、以及解决微服务架构负载均衡问题
    第一节:CDN、SLB、DNS(4层和7层)、IOT
    第三十节:Asp.Net Core中JWT刷新Token解决方案
    第六节:秒杀业务/超卖的几种解决思路
    第五节: Redis架构演变历程和cluster集群模式架构的搭建
  • 原文地址:https://www.cnblogs.com/TSHugh/p/8638050.html
Copyright © 2020-2023  润新知