求组合数 I
思路: 很明显,a和b 比较小,直接递推即可。
公式:C[i][j] = C[i-1][j] +C[i-1][j-1] 。 提供两种理解角度: 1、杨辉三角形(可自行百度)。2、组合公式理解, 从 i 里选 j 个的方案数由两部分,设k是 i 中某一个, 则选取 k 和不选取 k :C[i-1][j] + C[i-1][j-1]
#include<bits/stdc++.h>
using namespace std;
const int MAX = 2010, mod = 1e9+7;
int c[MAX][MAX];
void init()
{
for(int i=0;i<MAX;i++)
for(int j=0;j<=i;j++)
if(!j) c[i][j] = 1;
else c[i][j] = (c[i-1][j]+c[i-1][j-1])%mod;
}
int main()
{
init();
int T;
cin>>T;
while(T--){
int a,b;
cin>>a>>b;
cout<<c[a][b]<<endl;
}
return 0;
}
求组合数 II
思路: a和b 较大,采用逆元模式,由于n较大同时预处理。
公式:C(a , b) = a! * infact( b! ) * infact( (a-b)! )
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7,MAX = 1e5+7;
typedef long long LL;
int fact[MAX]; // 阶乘;
int infact[MAX]; // 阶乘的逆元;
int quick(int a,int b){
int ans =1;
while(b){
if(b&1) ans = (LL)ans* a%mod;
a = (LL)a*a%mod;
b>>=1;
}
return ans;
}
void init()
{
fact[0] = 1, infact[0] = 1;
for(int i=1;i<MAX;i++){
fact[i] = (LL)fact[i-1]* i %mod;
infact[i] = (LL)infact[i-1]* quick(i,mod-2)%mod; //这里 infact(ab)=infact(a)*infact(b);
}
}
int main()
{
init();
int T;
scanf("%d",&T);
while(T--){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",(LL) fact[a] * infact[b]%mod* infact[a-b]%mod ); //注意加 LL
}
return 0;
}
求组合数 III
思路: 由于a,b非常大而 p 却不大,很明显在考察Lucas定理:Lucas定理是用来求 c(n,m) mod p的值的,p为质数。
公式:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL a,b,p;
int quick(int a,int b){
int ans =1;
while(b){
if(b&1) ans = (LL) ans*a % p;
a = (LL)a*a % p;
b>>=1;
}
return ans;
}
int C(int a,int b){
int ans = 1;
for(int i= 1,j= a; i<=b ;i++,j--){
ans = (LL)ans *j % p;
ans = (LL)ans * quick(i,p-2)% p;
}
return ans;
}
int lucas(LL a,LL b){
if(a<p && b<p) return C(a,b) ;
return (LL)lucas(a/p,b/p) * C(a%p,b%p) % p; //递归调用 直到 t=0
}
int main()
{
int n;
cin>>n;
while(n--){
cin>>a>>b>>p;
cout<<lucas(a,b)<<endl;
}
return 0;
}
求组合数 IV
思路: 题目没有取模,且提示使用高精度,如果我们直接写的话,需要使用大数乘法和大数除法 两种大数运算,而且效率比较低,所以这里我们采用质因数分解法解决此问题( 详情请移步 阶乘分解)。
#include<bits/stdc++.h>
using namespace std;
const int MAX = 5010;
int primes[MAX] ,cnt;
bool st[MAX];
void init(int n)
{
for(int i=2;i<=n;i++){ //线性筛 (埃氏筛优化)
if(!st[i]) primes[cnt++] = i;
for(int j=0;primes[j]*i<=n;j++){
st[primes[j]*i] = 1;
if (i%primes[j]==0) break;
}
}
}
int get(int n,int p)
{
int ans=0; // 求 p 因子在n的阶乘中出现的次数(P ^ ans);
while(n){
ans+=n/p;
n/=p;
}
return ans;
}
vector<int> mul(vector<int> a,int x) //大数乘法
{
vector<int> ans;
int t = 0;
for(int i=0;i<a.size() || t ;i++){
if(i<a.size()) t += a[i]*x ;
ans.push_back(t%10);
t/=10;
}
return ans;
}
int main()
{
int a,b;
cin>>a>>b;
init(a);
vector<int> ans;
ans.push_back(1);
for(int i=0;i<cnt;i++){
int p = primes[i];
int num = get(a,p) - get(b,p) - get(a-b,p); // 由 a! / b!/ ( (a-b)! )求此质因子的次幂
for(int j=0;j<num;j++){
ans = mul(ans,p);
}
}
for(int i=ans.size()-1;i>=0;i--) cout<<ans[i];
cout<<endl;
return 0;
}