组合数取模学习笔记

组合数取模的话,之前多少会一些,能应付一般的题目,而这次遇到了模数为合数的题目,于是就又来学习了一发.
这次看到了一个比较不错的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\n",ans);
  return 0;
}

 

posted @ 2018-03-24 11:19  TS_Hugh  阅读(...)  评论(... 编辑 收藏