求组合数 I
给定n组询问,每组询问给定两个整数a,b,请你输出$C_a^b$ mod (1e9+7)的值。
输入格式
第一行包含整数n。
接下来n行,每行包含一组a和b。
输出格式
共n行,每行输出一个询问的解。
数据范围
1≤n≤10000,
1≤b≤a≤2000
输入样例:
3
3 1
5 3
2 2
输出样例:
3
10
1
分析:
这道题的数据范围较小,而且是多组输入,可以打表,将所有
C
a
b
C_a^b
Cab都预处理出来,处理过程运用求组合数的递推公式
C
a
b
=
C
a
−
1
b
+
C
a
−
1
b
−
1
C_a^b=C_{a-1}^b+C_{a-1}^{b-1}
Cab=Ca−1b+Ca−1b−1,代码较为容易实现。
代码如下
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=2e3+10;
const int mod=1e9+7;
int q[N][N];
void init()
{
for(int i=1;i<N;i++)
for(int j=0;j<=i;j++)
if(!j||j==i) q[i][j]=1;
else q[i][j]=(q[i-1][j]+q[i-1][j-1])%mod;
}
int main()
{
int n;
scanf("%d",&n);
init();
while(n--)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",q[a][b]);
}
return 0;
}
求组合数 II
给定n组询问,每组询问给定两个整数a,b,请你输出$C_a^b$ mod (1e9+7)的值。
输入格式
第一行包含整数n。
接下来n行,每行包含一组a和b。
输出格式
共n行,每行输出一个询问的解。
数据范围
1≤n≤10000,
1≤b≤a≤1e5
输入样例:
3
3 1
5 3
2 2
输出样例:
3
10
1
分析:
这道题的数据范围较大,不能够像组合数I打表预先处理出所有组合数的值,不过根据组合数公式
C
a
b
=
a
!
(
a
−
b
)
!
∗
b
!
C_a^b=\frac{a!}{(a-b)!*b!}
Cab=(a−b)!∗b!a!我们可以将其阶乘预处理出来,由于要
m
o
d
1
e
9
+
7
mod\ 1e9+7
mod 1e9+7,并且需要除法,这里需要求逆元,将除法转化为乘法。这样就能够解出来了。递推求出前缀积和逆元,逆元根据
(
a
∗
b
)
−
1
=
a
−
1
∗
b
−
1
(a*b)^{-1}=a^{-1}*b^{-1}
(a∗b)−1=a−1∗b−1来求
代码如下:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int mod=1e9+7;
int q[N],f[N];
int qmi(int a,int b)//费马小定理,快速幂
{
int res=1;
while(b)
{
if(b&1)
res=(ll)res*a%mod;
a=(ll)a*a%mod;
b>>=1;
}
return res;
}
void init()
{
q[0]=f[0]=1;
for(int i=1;i<N;i++)
{
q[i]=(ll)q[i-1]*i%mod;//处理出所有的前缀乘
f[i]=(ll)f[i-1]*qmi(i,mod-2)%mod;//逆元$(a*b)^{-1}=a^{-1}*b^{-1}$
}
}
int main()
{
init();
int t;
scanf("%d",&t);
while(t--)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",(ll)q[a]*f[a-b]%mod*f[b]%mod);
}
return 0;
}
求组合数 III
给定n组询问,每组询问给定三个整数a,b,p,其中p是质数,请你输出$C_a^b$ mod p的值。
输入格式
第一行包含整数n。
接下来n行,每行包含一组a,b,p。
输出格式
共n行,每行输出一个询问的解。
数据范围
1≤n≤20,
1≤b≤a≤1e18,
1≤p≤1e5,
输入样例:
3
5 3 7
3 1 5
6 4 13
输出样例:
3
3
2
分析:
这道题的数据范围较大且P不确定,不能够预处理,P的范围为1≤p≤1e5,这道题主要用到了Lucas定理,有公式
C
a
b
m
o
d
p
=
C
a
m
o
d
p
b
m
o
d
p
∗
C
a
/
p
b
/
p
C_a^b\ mod\ p=C_{a\ mod\ p}^{b\ mod\ p}*C_{a/p}^{b/p}
Cab mod p=Ca mod pb mod p∗Ca/pb/p,根据这个公式可以求得组合数。
代码如下:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll a,b;
int p;
int qmi(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=(ll)res*a%p;
a=(ll)a*a%p;
b>>=1;
}
return res;
}
int C(int a,int b)
{
int res=1;
for(int i=1,j=a;i<=b;j--,i++)
{
res=(ll)res*j%p*qmi(i,p-2)%p;
}
return res;
}
int lucas(ll a,ll b)
{
if(a<p&&b<p) return C(a,b);
else return (ll)C(a%p,b%p)*lucas(a/p,b/p)%p;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld%d",&a,&b,&p);
printf("%d\n",lucas(a,b));
}
return 0;
}
求组合数 IV
输入a,b,求$C_a^b$的值。
注意结果可能很大,需要使用高精度计算。
输入格式
共一行,包含两个整数a和b。
输出格式
共一行,输出$C_a^b$的值。
数据范围
1≤b≤a≤5000
输入样例:
5 3
输出样例:
10
分析:
这道题不需要对结果取余,结果的范围较大,需要用到高精度算法这里我们可以将运算过程简化,如果做大数乘法和除法得话,比较麻烦,可以将
a
!
a!
a! ,
(
a
−
b
)
!
(a-b)!
(a−b)! ,
b
!
b!
b!质因子分解,消除其相同的质因子,只需要进行大数乘法即可。
代码如下:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N=5010;
int primes[N],cnt=0;
int sum[N];
bool f[N];
void get_primes(int n)//线性筛
{
for(int i=2;i<=n;i++)
{
if(!f[i]) primes[cnt++]=i;
for(int j=0;primes[j]<=n/i;j++)
{
f[primes[j]*i]=true;
if(i%primes[j]==0) break;
}
}
}
int get_p(int n,int x)//计算n!中有几个x
{
int res=0;
while(n)
{
res+=n/x;
n/=x;
}
return res;
}
vector<int> mul(vector<int> res,int x)//大数乘法
{
int t=0;
vector<int>c;
for(int i=0;i<res.size();i++)
{
t+=res[i]*x;
c.push_back(t%10);
t/=10;
}
while(t)
{
c.push_back(t%10);
t/=10;
}
return c;
}
int main()
{
int a,b;
scanf("%d%d",&a,&b);
get_primes(a);//线性筛,处理出所有质数
for(int i=0;i<cnt;i++)//获得每个质数的数量
{
sum[i]=get_p(a,primes[i])-get_p(a-b,primes[i])-get_p(b,primes[i]);
}
vector<int>res;
res.push_back(1);
for(int i=0;i<=cnt;i++)
for(int j=0;j<sum[i];j++)//大数运算
res=mul(res,primes[i]);
for(int i=res.size()-1;i>=0;i--)
printf("%d",res[i]);
printf("\n");
return 0;
}