program1 n!%P(P为质数)
我们发现n! mod P的计算过程是以P为周期的的,举例如下:
n = 10, P = 3
n! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10
= 1 * 2 *
4 * 5 *
7 * 8 *
10 *
3 * 6 * 9
= (1 * 2)^3 *
1*
3^3 * (1 * 2 * 3)
最后一步中的1 * 2 *3可递归处理。
因为P的倍数与P不互质,所以P的倍数不能直接乘入答案,应当用一个计数器变量g来保存答案中因子P的个数。
我们提前预处理出fac[i] = 1 * 2 * 3 * … * (i – 1) * i mod P,函数calcfac(n)返回n! mod P的值,power(a, b, c)返回ab mod c的值,注意,calcfac(n)算出来的值不包含因子P,我们另外把因子P的个数另外存在g中。
代码如下:
typedef long long LL;
LL calcfac(LL n)
{
if (n < P) return fac[n];
LL s = n / P, t = n % P;
LL res = power(fac[P - 1], s, P); //fac[P - 1]重复出现了s次
res = res * fac[t] % P; //除去s次fac[P – 1]外,剩下的零头
g += n / P; //提出n / P个因子P
res = res* calcfac(n / P) % P; //递归处理
return res;
}
program2 C(m,n)%P(P为质数)
运用program1,算出m!%P,n!%P和(m-n)!%P,已知这三者的答案中因子P的个数为g1,g2,g3。
我们可以得到:
我们不假思索地认为g1-g2-g3>=0(否则C(m,n)算出来就是分数了),而且容易知道calcfac(n)*falcfac(m-n)与P互质(我们除去了P因子)所以可以变成:
这样就可以直接算了。
program3 C(m,n)%P(P不一定为质数)
基本思路与program2相同。
对P进行质因数分解,得到P = p1c1 * p2c2 * … * ptct。
对于每个1≤i≤t,以pici为模运行program2,最后用中国剩余定理合并。这里pici不一定为质数,不过只需对原算法稍加修改即可。令modi = pici,fac[i] = 除去pi的倍数外i的阶乘。例如pi = 3,ci = 2,那么fac[10] = 1 * 2 * 4 * 5 * 7 * 8 * 10,除去了3的倍数3、6和9。阶乘依然是以modi为周期的,calcfac(n)与program2主体相同,只是统计因子个数时,应使用ret += n / pi而不是ret += n / modi,递归处理时也应该是calcfac(n / pi)而不是calcfac(n / modi)。
当然,这个算法也有些局限,因为要求fac数组,所以pici要比较小,如果P是一个大质数就不行了。
#include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stack> #include<map> #include<utility> #include<set> #include<bitset> #include<vector> #include<functional> using namespace std; typedef long long LL; typedef double DB; typedef pair<int,int> PII; #define mmst(a,v) memset(a,v,sizeof(a)) #define mmcy(a,b) memcpy(a,b,sizeof(a)) #define re(i,a,b) for(i=a;i<=b;i++) #define red(i,a,b) for(i=a;i>=b;i--) #define fi first #define se second template<class T>inline T sqr(T x){return x*x;} template<class T>inline void upmin(T &t,T tmp){if(t>tmp)t=tmp;} template<class T>inline void upmax(T &t,T tmp){if(t<tmp)t=tmp;} const DB EPS=1e-9; inline int dblcmp(DB x){if(abs(x)<EPS)return 0;return(x>0)?1:-1;} inline void SetOpen(string s) { freopen((s+".in").c_str(),"r",stdin); freopen((s+".out").c_str(),"w",stdout); } inline int Getin_Int() { int res=0,flag=1;char z; for(z=getchar();z!=EOF && z!=‘-‘ && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z==‘-‘){flag=-flag;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-‘0‘,z=getchar()); return res*flag; } inline LL Getin_LL() { LL res=0,flag=1;char z; for(z=getchar();z!=EOF && z!=‘-‘ && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z==‘-‘){flag=-flag;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-‘0‘,z=getchar()); return res*flag; } const LL maxpici=100000; const LL maxcnt=100; LL n,m,P; LL cnt,p[maxcnt+10],c[maxcnt+10],mod[maxcnt+10],euler[maxcnt+10],fac[maxcnt+10][maxpici+10]; LL ans; inline LL power(LL a,LL b,LL Mod) { LL x=1,y=a; while(b!=0){if(b&1)x=x*y%Mod;if(b==1)break;y=y*y%Mod;b/=2;} return x; } inline LL calfac(LL N,LL id,LL &g) { if(N<p[id])return fac[id][N]; LL s=N/mod[id],t=N%mod[id],res=1; res=res*power(fac[id][mod[id]-1],s,mod[id])%mod[id]; res=res*fac[id][t]%mod[id]; g+=N/p[id]; res=res*calfac(N/p[id],id,g)%mod[id]; return res; } inline LL solve(LL id) { LL g1=0,g2=0,g3=0; LL fenzi=calfac(m,id,g1),fenmo=calfac(n,id,g2)*calfac(m-n,id,g3)%mod[id]; return fenzi*power(fenmo,euler[id]-1,mod[id])%mod[id]*power(p[id],g1-g2-g3,mod[id])%mod[id]; } inline void extend_gcd(LL a,LL &x,LL b,LL &y) { if(b==0){x=1;y=0;return;} LL dx,dy; extend_gcd(b,dx,a%b,dy); x=dy; y=dx-a/b*dy; } int main() { SetOpen("program"); LL i,j,temp; m=Getin_Int();n=Getin_Int();P=Getin_Int(); if(m<n){puts("0\n");return 0;} temp=P; re(i,2,maxpici)if(temp%i==0) { ++cnt; p[cnt]=i; mod[cnt]=1; while(temp%i==0){c[cnt]++;mod[cnt]*=i;temp/=i;} euler[cnt]=mod[cnt]/p[cnt]*(p[cnt]-1); fac[cnt][0]=1; re(j,1,mod[cnt]-1)fac[cnt][j]=(j%p[cnt]==0)?fac[cnt][j-1]:fac[cnt][j-1]*j%mod[cnt]; } ans=0; re(i,1,cnt) { temp=solve(i); LL a=P/mod[i],x,b=mod[i],y; extend_gcd(a,x,b,y); ans=(ans+temp*a%P*x%P)%P; } ans=(ans%P+P)%P; cout<<ans<<endl; return 0; }