求組合數C(n,m) % mod的幾種方法
阿新 • • 發佈:2018-11-11
演算法一:乘法逆元,在m,n和mod比較小的情況下適用
乘法逆元:(a/b)% mod = a * b^(mod-2),mod為素數
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<queue> #include<stack> #include<vector> #define LL long long using namespace std; const int MOD = 9982; LL pow(LL x) { LL n = MOD-2; LL res=1; while(n>0) { if(n & 1) res=res*x%MOD; x=x*x%MOD; n>>=1; } return res%MOD; } LL C(LL n,LL m) { if(m < 0)return 0; if(n < m)return 0; if(m > n-m) m = n-m; LL up = 1, down = 1; for(LL i = 0 ; i < m ; i ++){ up = up * (n-i) % MOD; //分子 down = down * (i+1) % MOD; //分母 } return up * pow(down) % MOD; //乘法逆元 } int main() { int n,m; scanf("%d%d",&n,&m); LL ans = C(n,m)%MOD; printf("%lld\n",ans); return 0; }
演算法二:Lucas定理 + 乘法逆元,適用於mod為素數且大小為10^5左右
Lucas定理:A、B是非負整數,p是質數。A B寫成p進位制:A=a[n]a[n-1]…a[0],B=b[n]b[n-1]…b[0]。
則組合數C(A,B)與C(a[n],b[n])C(a[n-1],b[n-1])…*C(a[0],b[0]) mod p同餘
即:Lucas(n,m,p)=C(n%p,m%p)*Lucas(n/p,m/p,p)
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<queue> #include<stack> #include<vector> #define LL long long using namespace std; const int MOD = 100000; LL pow(LL x) { LL n = MOD-2; LL res=1; while(n>0) { if(n & 1) res=res*x%MOD; x=x*x%MOD; n>>=1; } return res%MOD; } LL C(LL n,LL m) { if(m < 0)return 0; if(n < m)return 0; if(m > n-m) m = n-m; LL up = 1, down = 1; for(LL i = 0 ; i < m ; i ++){ up = up * (n-i) % MOD; //分子 down = down * (i+1) % MOD; //分母 } return up * pow(down) % MOD; //乘法逆元 } //當n和m比較大,mod是素數且比較小的時候(10^5左右),通過Lucas定理計算 LL Lucas(LL n, LL m) { if(m == 0) return 1; return C(n%MOD,m%MOD)*Lucas(n/MOD,m/MOD)%MOD; } int main() { int T,n,m; LL ans; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); ans = Lucas(n,m)%MOD; printf("%lld\n",ans); } return 0; }
演算法三:預處理 + 乘法逆元,適用於mod為素數且比較大的時候(超過10^5)
預處理的時候注意: m!的MOD次方 = (m-1)!的MOD次方 * m的MOD次方
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<queue> #include<stack> #include<vector> #define LL long long #define maxn 1000000 using namespace std; const int MOD = 998244353; LL fact[maxn+5]; //階乘 LL a[maxn+10]; // 乘法逆元 //LL inv[maxn+10]; //快速冪 LL pow(LL x) { LL n = MOD-2; LL res=1; while(n>0) { if(n%2==1) res=res*x%MOD; x=x*x%MOD; n>>=1; } return res; } void init(){ a[0] = a[1] = 1; fact[0] = fact[1] = 1; // inv[1] = 1; for(int i = 2; i <= 1000005; i++) { fact[i] = fact[i-1] * i % MOD; a[i] = a[i-1] * pow(i) % MOD; //m!的MOD次方 = (m-1)!的MOD次方 * m的MOD次方 // inv[i] = (MOD - MOD/i)*inv[MOD%i]%MOD; // a[i] = a[i-1] * inv[i] % MOD; } } LL C(int n, int m){ //乘法逆元 if(n<0||m<0||n<m)return 0; return fact[n]*a[n-m]%MOD*a[m]%MOD; } int main() { int T,n,m; LL ans; init();//預處理 scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); ans = C(n,m)%MOD; printf("%lld\n",ans); } return 0; }