组合数取模 【数论】
本人水平有限,题解不到为处,请多多谅解
本蒟蒻谢谢大家观看
题目:
3942: 组合数取模
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 113 Solved: 39
[Submit][Status][Web Board]
Description
给出N,M,P,求C(N,M) Mod P
1<=M<=N<=10^6,1<=P<=10^5,P可能为合数
1<=M<=N<=10^6,1<=P<=10^5,P可能为合数
Input
一行给出N,M,P
Output
如题
Sample Input
5 2 3
Sample Output
1
HINT
如果直接用公式暴力的话,显然会爆出long long类型,直接RE。
所以要优化,我们可以利用组合数公式来进一步推倒。
我们可以将n!写成几个质数幂的积的形式。再利用逆元不断取模即可。
我们可以先把能约的先约掉,直接指数相减即可。
例如:5!=1*2*3*4*5 可以写成:(2^3)*(3^1)*(5^1) 又可以写成:(2^3)%p*(3^1)%p*(5^1)%p 即可
我们可以先用欧拉线筛求出质数,再把质数进行求其指数,最后用快速幂还原成常数即可。
在上代码之前先来解释一下我的变量:
Isprime表示质数的顺序。例如:2为所有质数的第一个,所以Isprime[2]==1,
3为所有质数的第二个,所以Isprime[3]==2 ……
primenum表示在n以内有多少个质数。例如:当n==10时,primenum[10]==4 因为10以内有4个质数。
sum表示当前质数的指数为多少。例如:2^3 为sum[2]==3
code:
1 #include<bits/stdc++.h> 2 #pragma GCC optimize(3) 3 #define int long long 4 const int N=1e6+10; 5 using namespace std; 6 int n,m,p; 7 int Isprime[N],primenum[N],sum[N]; 8 bool vis[N]; 9 int ans=1; 10 int tot=0; 11 void inint(){ 12 freopen("com.in","r",stdin); 13 freopen("com.out","w",stdout); 14 } 15 inline int read(){ 16 int x=0,f=1;char ch=getchar(); 17 while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} 18 while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} 19 return x*f; 20 } 21 inline void write(int x) 22 { 23 if(x<0)x=-x,putchar('-'); 24 if(x>9)write(x/10); 25 putchar(x%10+'0'); 26 } 27 int quick_mi(int a,int b){ 28 if(b==0)return 1; 29 if(b%2==1)return quick_mi(a*a,b/2)*a; 30 else return quick_mi(a*a,b/2); 31 } 32 void PRIME(int x){ 33 for(int i=2;i<=x;i++){ 34 primenum[i]=primenum[i-1]; 35 if(!vis[i]){ 36 Isprime[++tot]=i; 37 primenum[i]++; 38 } 39 for(int j=1;j<=tot&&i*Isprime[j]<=x;j++){ 40 vis[i*Isprime[j]]=true; 41 if(i%Isprime[j]==0)break; 42 } 43 } 44 return ; 45 } 46 void solve(int x,int y,int z,int mod){ 47 for(int i=1;i<=primenum[x];i++){ 48 int yzl=x; 49 while(yzl!=0){ 50 sum[i]+=yzl/Isprime[i]; 51 yzl=yzl/Isprime[i]; 52 } 53 } 54 for(int i=1;i<=primenum[y];i++){ 55 int yzl=y; 56 while(yzl!=0){ 57 sum[i]-=yzl/Isprime[i]; 58 yzl=yzl/Isprime[i]; 59 } 60 } 61 for(int i=1;i<=primenum[z];i++){ 62 int yzl=z; 63 while(yzl!=0){ 64 sum[i]-=yzl/Isprime[i]; 65 yzl=yzl/Isprime[i]; 66 } 67 } 68 for(int i=1;i<=primenum[x];i++){ 69 ans=ans*(quick_mi(Isprime[i],sum[i])%mod); 70 ans%=mod; 71 } 72 return ; 73 } 74 signed main() 75 { 76 //inint(); 77 n=read(),m=read(),p=read(); 78 PRIME(n); 79 solve(n,m,n-m,p); 80 printf("%lld\n",ans); 81 return 0; 82 }