https://ac.nowcoder.com/discuss/187813?type=101&order=0&pos=1&page=0
https://blog.csdn.net/shadandeajian/article/details/82084087
1.简单法---适合n,m很小
#include<bits/stdc++.h> using namespace std; const int MAXN = 1000; int C[MAXN+1][MAXN+1]; //求排列组合数C(m,n) 上面为m,下面为n m<n //C(m,n)=n!/m!/(n-m)!=n*(n-1)*..*(n-m+1)/m!. int baoli_C(int m,int n) //暴力法这里n<=15 { int summ=1,sumn=1; //其实算C(m,n)只要计算min(m,n-m)次就可以了 if(m>n-m) m=n-m; for(int i=1;i<=m;i++){ summ*=i; sumn=sumn*(n-i+1); } return sumn/summ; } void dabiao_C(){ //打表,数据为int,注意溢出数据 n<60 //C(n, m) = C(n -1, m - 1) + C(n - 1, m) for(int i=0;i<MAXN;i++) { // C[i][0]=1; C[0][i]=0;//该写法顺序是错误的,因为这样写C[0][0]=0; C[0][i]=0;C[i][0]=1; } for(int i=1;i<MAXN;i++) for(int j=1;j<MAXN;j++) C[i][j]=C[i-1][j-1]+C[i-1][j]; } int main(){ dabiao_C(); int m,n; while(cin>>m>>n){ //m<n cout<<C[m][n]<<endl; } }
2.Lucas定理求组合数
组合数C(n, m) % p
= (n!/m!/(n-m)!)%mod 组合数公式
= n!*inv(m!*(n-m)!)%mod 转化式子
= n!*(m!*((n-m)!)^(mod-2))%mod 由于p是素数,有费马小定理可知,m! * (n - m)! 关于p的逆元就是m! * (n - m)!的p-2次方。
=(n*(n-1)*..*(n-m+1) / m!) %mod.==( (n*(n-1)*..*(n-m+1)) * (m^(mod-2)) ) %mod.
第一种情况:p是素数,且p较小,采用打表---打表记录 阶乘%mod的值 --- n!*(m!*((n-m)!)^(mod-2))%mod
#include<bits/stdc++.h> using namespace std; #define ll long long const int MAXN = 1000; #define mod 998244353 const int maxn = 1e5+10; ll fac[maxn];//maxn应该小于1e5,这样Lucas定理才适用 void Init(){ //对阶乘打表 fac[0]=1; for(int i=1;i<=maxn-5;i++){ fac[i]=fac[i-1]*i%mod; } } ll quickpow(ll a,ll b){ ll t=a,ans=1; while(b!=0){ if(b&1==1) ans=(ans*t)%mod; t=t*t%mod; b>>=1; } return ans%mod; } ll C(ll n,ll m){ if(m>n) return 0; return fac[n]*quickpow(fac[m]*fac[n-m],mod-2)%mod; } ll Lucas(ll n,ll m){ if(m==0) return 1; return Lucas(n/mod,m/mod)*C(n%mod,m%mod)%mod; } int main(){ ll n,m; Init(); while(cin>>n>>m){ cout<<C(n,m)<<endl; } }
第二种情况:p是素数,且p比较大,不好打表 ---(n*(n-1)*..*(n-m+1) / m!) %mod.
#include<bits/stdc++.h> using namespace std; #define ll long long const int MAXN = 1000; #define mod 998244353 ll pow(ll a, ll b, ll m) { ll ans = 1; a %= m; while(b) { if(b & 1)ans = (ans % m) * (a % m) % m; b /= 2; a = (a % m) * (a % m) % m; } ans %= m; return ans; } ll inv(ll x, ll p)//x关于p的逆元,p为素数 { return pow(x, p - 2, p); } ll C(ll n, ll m, ll p)//组合数C(n, m) % p = (n!/m!/(n-m)!)%mod = n!*inv(m!*(n-m)!)%mod = n!*(m!*((n-m)!)^(mod-2))%mod { if(m > n)return 0; ll up = 1, down = 1;//分子分母; for(int i = n - m + 1; i <= n; i++)up = up * i % p; for(int i = 1; i <= m; i++)down = down * i % p; return up * inv(down, p) % p; } ll Lucas(ll n, ll m, ll p) { if(m == 0)return 1; return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p; } int main(){ int m,n; while(cin>>m>>n){ //m<n cout<<Lucas(m,n,mod)<<endl; } }
第三种情况:p不是素数,且n,m比较大---扩展Lucas定理:https://www.cnblogs.com/fzl194/p/9095177.html
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e6 + 10; const int mod = 1e9 + 7; ll pow(ll a, ll b, ll m) { ll ans = 1; a %= m; while(b) { if(b & 1)ans = (ans % m) * (a % m) % m; b /= 2; a = (a % m) * (a % m) % m; } ans %= m; return ans; } ll extgcd(ll a, ll b, ll& x, ll& y) //求解ax+by=gcd(a, b) //返回值为gcd(a, b) { ll d = a; if(b) { d = extgcd(b, a % b, y, x); y -= (a / b) * x; } else x = 1, y = 0; return d; } ll mod_inverse(ll a, ll m) //求解a关于模上m的逆元 //返回-1表示逆元不存在 { ll x, y; ll d = extgcd(a, m, x, y); return d == 1 ? (m + x % m) % m : -1; } ll Mul(ll n, ll pi, ll pk)//计算n! mod pk的部分值 pk为pi的ki次方 //算出的答案不包括pi的幂的那一部分 { if(!n)return 1; ll ans = 1; if(n / pk) { for(ll i = 2; i <= pk; i++) //求出循环节乘积 if(i % pi)ans = ans * i % pk; ans = pow(ans, n / pk, pk); //循环节次数为n / pk } for(ll i = 2; i <= n % pk; i++) if(i % pi)ans = ans * i % pk; return ans * Mul(n / pi, pi, pk) % pk;//递归求解 } ll C(ll n, ll m, ll p, ll pi, ll pk)//计算组合数C(n, m) mod pk的值 pk为pi的ki次方 { if(m > n)return 0; ll a = Mul(n, pi, pk), b = Mul(m, pi, pk), c = Mul(n - m, pi, pk); ll k = 0, ans;//k为pi的幂值 for(ll i = n; i; i /= pi)k += i / pi; for(ll i = m; i; i /= pi)k -= i / pi; for(ll i = n - m; i; i /= pi)k -= i / pi; ans = a * mod_inverse(b, pk) % pk * mod_inverse(c, pk) % pk * pow(pi, k, pk) % pk;//ans就是n! mod pk的值 ans = ans * (p / pk) % p * mod_inverse(p / pk, pk) % p;//此时用剩余定理合并解 return ans; } ll Lucas(ll n, ll m, ll p) { ll x = p; ll ans = 0; for(ll i = 2; i <= p; i++) { if(x % i == 0) { ll pk = 1; while(x % i == 0)pk *= i, x /= i; ans = (ans + C(n, m, p, i, pk)) % p; } } return ans; } int main() { ll n, m, p; while(cin >> n >> m >> p) { cout<<Lucas(n, m, p)<<endl; } return 0; }
原文地址:https://www.cnblogs.com/Aiahtwo/p/11132830.html
时间: 2024-11-07 07:31:10