那么要求模数为质数的lucas定理就不能用了
我们只能直接算阶乘了
但是对组合数分母来说与模数不互质的话逆元是不存在的
记当前问题为C(n,k) % m
把m分解质因数后得到的m=p1^r1 * p2^r2 *......
C(n,k) % p1^r1
C(n,k) % p2^r2
......
之后把所有结果用中国剩余定理合并就得到原来的结果了
那么现在的问题就是怎么求 C(n,k) % p^r
首先还是要解决互质的问题我们才能算分母的逆元
我们计算出 n! k! (n-k)!分别含有多少个p因子,就可以算出该组合数总的p因子数,如果大于等于r说明是p^r的倍数,取模得0,如果不是那还要往下算
算一个阶乘含多少个p因子的方法是:n/p+n/p/p+n/p/p/p+......
然后就是要算剩下的与p互质的部分了,也就是要算一个不含p因子的阶乘
记mod=p^r
我们可以先预处理出到mod的阶乘,遇到p的倍数先暂时忽略,乘1就可以了,其他的照常乘进去
然后对于n!来说,每mod个数的乘积对mod取模是相等的,就是说 1*2*...*mod == (mod+1)*(mod+2)*(2mod)
当然我说的都是除去含p因子的数之后的,为了方便描述我就先直接这么说了
那么就可以看n能被mod分成多少份,就得到n!=(mod!)^(n/mod) * (n%mod)! * (含p因子的数除去p因子后剩下的东西)
现在问题又缩小到怎么求含p因子的数除去p因子后剩下的东西了
因为 p 除去p剩下1 ,2p除去p剩下2 ,。。。。。。
因为剩下的东西要乘起来,所以就相当于能被p分成多少份,就是它的阶乘,即(n/p)!
这个东西也是要除去含p的因子的,也就是那些p的高次方的数除去一个p后还会剩下一些p
那么就跟我们上面的过程类似,可以n/=p循环求解,把所有结果乘一起
现在得到了不含p因子的阶乘了,那么分母就可以通过欧拉定理求逆了
把分子和分母的逆和一开始求出来的该组合数总的p因子数全部乘一起就得到一条方程的结果了
好了之后剩下的就是中国剩余定理合并所有方程的结果了。
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<string> #include<iomanip> #pragma comment(linker, "/STACK:1024000000,1024000000") #include<vector> #include<set> #include<map> #include<queue> #include<list> using namespace std; typedef long long LL; typedef unsigned long long ULL; #define rep(i,k,n) for(int i=(k);i<=(n);i++) #define red(i,k,n) for(int i=(k);i>=(n);i--) #define sqr(x) ((x)*(x)) #define clr(x,y) memset((x),(y),sizeof(x)) #define MAX(a,b) ((a)>(b)?(a):(b)) #define MIN(a,b) ((a)>(b)?(b):(a)) LL n,k,m; LL p[25],r[25]; int pcnt; LL a[25]; LL quipow(LL x,LL k,LL mod) { LL ret=1; while(k) { if(k&1)ret=ret*x%mod; x=x*x%mod; k>>=1; } return ret; } int quipow(int x,int k) { int ret=1; while(k) { if(k&1)ret=ret*x; x=x*x; k>>=1; } return ret; } void getprime(int m) { clr(p,0); clr(r,0); pcnt=0; for(LL i=2;i*i<=m;i++) { if(m%i==0) { p[++pcnt]=i; while(m%i==0){m/=i;r[pcnt]++;} } } if(m>1) { p[++pcnt]=m; r[pcnt]=1; } } LL f[1000010]; void pref(int p,int mod) { clr(f,0); f[0]=f[1]=1; rep(i,2,mod-1)f[i]=f[i-1]*(i%p?i:1)%mod; } LL getp(LL n,LL p) { LL ret=0; while(n) ret+=(n/=p); return ret; } LL getf(LL n,LL mod,LL p) { LL ret=1; while(n) { ret=ret*quipow(f[mod-1],n/mod,mod) %mod *f[n%mod] %mod ; n/=p; } return ret; } LL getc(LL n,LL m,LL p,LL r) { LL mod=quipow(p,r); pref(p,mod); LL nump=getp(n,p)-getp(m,p)-getp(n-m,p); if(nump>=r)return 0; return quipow(p,nump,mod)*getf(n,mod,p)%mod *quipow(getf(m,mod,p)*getf(n-m,mod,p)%mod,(p-1)*quipow(p,r-1)-1,mod)%mod ; } LL exgcd(LL a,LL b,LL &x,LL &y) { if(!b) { x=1; y=0; return a; } else { LL g=exgcd(b,a%b,y,x); y-=x*(a/b); return g; } } int CR(int cnt) { LL ret=0,x,y; rep(i,1,cnt) { exgcd(m/p[i],p[i],x,y); ret=(ret+m/p[i]*x*a[i])%m; } return (ret+m)%m; } int main() { //#define LOCAL #ifdef LOCAL freopen("e:\\read.txt","r",stdin); //freopen("e:\\write.txt","w",stdout); #endif while(cin>>n>>k>>m) { getprime(m); rep(i,1,pcnt) { a[i]=getc(n,k,p[i],r[i]); p[i]=quipow(p[i],r[i]); } cout<<CR(pcnt)<<endl; } return 0; }