求组合数C(m,n)的多种计算方法
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; }
代码示例:
#include<bits/stdc++.h> using namespace std; #define mod 998244353 #define ll long long ll fac[100005]; void Init(){ fac[0]=1; for(int i=1;i<=100000;i++) fac[i]=(fac[i-1]*i)%mod; } long long quickmod(int x,int y) { if(x==0) return 0; long long t=x,ans=1; while(y!=0) { if(y&1==1) ans=(ans*t)%mod; t=(t*t)%mod; y>>=1; } return ans; } ll C(int n,int m){ return fac[n]/fac[n-m]*quickmod(fac[m],mod-2)%mod; } ll Lucas(int n,int m){ if(m==0) return 1; return Lucas(n/mod,m/mod)*C(n%mod,m%mod)%mod; } int main() { Init(); int T; scanf("%d",&T); int a,b,n,m; while(T--){ scanf("%d%d%d%d",&a,&b,&n,&m); long long h,ans; h=Lucas(n-1,m-1); // cout<<quickmod(a,n-m)%mod<<endl; // cout<<quickmod(b,m-1)%mod<<endl; ans=quickmod(a,n-m)*quickmod(b,m-1)%mod*h; cout<<ans%mod<<endl; } } /* 10 2 0 5 3 2 3 3 5 5 1 4 6 5 1 6 4 0 2 6 4 2 0 6 4 */
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!