[组合数取模] 方法汇总
1.利用整数唯一分解定理,求(n+1-m) * (n+m)! / ( m! * (n+1)! )
不论什么正整数都有且仅仅有一种方法写出其素因子幂相乘的形式。比方18= 2 * 3^2
A=(p1^k1)*(p2^k2)*(p3^k3)*(p4^k4)*......*(pn^kn) pi为素数
还有把阶层看作一个数,比m! 如何求m!里面素数2的指数呢?
cnt=0; while(m) { m/=2; cnt+=m; } 就能够了,为什么呢?考虑m=4,则m!= 4*3*2*1, 第一次m/=2。是计算m!里面有多少个数能整除2的(有4,2),所以cnt+=2,有两个数贡献
了两个素数2,接下来第二次m/=2。是计算m!里面有多少个数能整除4的,有1个数又贡献了一个素数2.
所以先预先筛出最大范围内的素数,然后考虑每一个素数就好了,关键是求出整个式子的该素数的指数是多少。
模板:
bool isprime[maxn*2+10]; int prime[maxn*2+10]; int len=0;//素数的个数 int n,m; void sieve(int n)//筛n以内的素数 { for(int i=0;i<=n;i++) isprime[i]=1; isprime[0]=isprime[1]=0; for(int i=2;i<=n;i++) if(isprime[i]) { prime[len++]=i; for(int j=2*i;j<=n;j+=i) isprime[j]=0; } } int cal(int p,int n)//计算n!里面有多少个p相乘 { int ans=0; while(n) { n/=p; ans+=n; } return ans; } int main() { sieve(maxn*2); long long ans=1;//记得要用long long cin>>n>>m; int nm=n+1-m; for(int i=0;i<len&&prime[i]<=(n+m);i++)//prime[i]<=(n+m)是由于拆成素数幂相乘的形式该素数不会大于n+m,最大(n+m)! (n+m)*(n+m-1)*(n+m-2)..... { int cnt=0;//分解为素数prime[i]的指数是多少 while(nm%prime[i]==0)//nm中有多少个prime[i],也就是把nm分解后prime[i]的指数 { nm/=prime[i]; cnt++; } cnt=cnt+cal(prime[i],n+m)-cal(prime[i],m)-cal(prime[i],n+1);//加上分子的指数再减去分母的指数 for(int j=1;j<=cnt;j++) { ans=ans*prime[i]; if(ans>=mod) ans%=mod; } } cout<<ans<<endl; return 0; }