885. 求组合数 I
给定 n
组询问,每组询问给定两个整数 a,b,请你输出 Cbamod(109+7)
的值。
输入格式
第一行包含整数 n
。
接下来 n
行,每行包含一组 a 和 b
。
输出格式
共 n
行,每行输出一个询问的解。
数据范围
1≤n≤10000
,
1≤b≤a≤2000
输入样例:
3
3 1
5 3
2 2
输出样例:
3
10
1
coding:
由于1 ≤ b ≤ a ≤ 2000;所以可以用二维数组把所有组合数的值都存储下来,
/**
* 由于1 ≤ b ≤ a ≤ 2000;所以可以用二维数组把所有组合数的值都存储下来,
*/
#include <iostream>
using namespace std;
const int maxn=2010 , mod=1e9+7;
int C[maxn][maxn];
void init()
{
for(int i=0;i<maxn;++i)
{
C[i][0]=1;
C[i][i]=1;
}
for(int i=2;i<maxn;++i)
for(int j=1;j<=i/2;++j)
{
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
C[i][i-j]=C[i][j];
}
}
int main()
{
init();
int n;
cin >> n;
while (n -- )
{
int a,b;
cin >> a >> b;
cout << C[a][b] << endl;
}
return 0;
}
886. 求组合数 II
给定 n
组询问,每组询问给定两个整数 a,b,请你输出 Cbamod(109+7)
的值。
输入格式
第一行包含整数 n
。
接下来 n
行,每行包含一组 a 和 b
。
输出格式
共 n
行,每行输出一个询问的解。
数据范围
1≤n≤10000
,
1≤b≤a≤105
输入样例:
3
3 1
5 3
2 2
输出样例:
3
10
1
假设 a,b,m都是整数,m>1,且ab与1模m同余,则a与b互为模m的逆元。
* 所以当m为质数的时候,a的m-2次方就是a模m的逆元。(费马小定理)。
*
* C(a,b)%mod= a!/(b!*(a-b)!)%mod;
* 由于1 ≤ b ≤ a ≤ 10^5,所以可以预处理出fac[maxn],infac[maxn](阶乘的结果,阶乘的逆元的结果)
* 结果直接用逆元进行计算。
/**
* 假设 a,b,m都是整数,m>1,且ab与1模m同余,则a与b互为模m的逆元。
* 所以当m为质数的时候,a的m-2次方就是a模m的逆元。(费马小定理)。
*
* C(a,b)%mod= a!/(b!*(a-b)!)%mod;
* 由于1 ≤ b ≤ a ≤ 10^5,所以可以预处理出fac[maxn],infac[maxn](阶乘的结果,阶乘的逆元的结果)
* 结果直接用逆元进行计算。
*/
#include <iostream>
using namespace std;
typedef long long LL;
const int maxn=1e5+10,mod=1e9+7;
int fac[maxn],infac[maxn];
int qmi(int a,int b,int p) //a^b%p
{
int res=1;
while(b)
{
if(b&1)
res=(LL)res*a%p;
a=(LL)a*a%p;
b>>=1;
}
return res;
}
void init()
{
fac[0]=infac[0]=1;
for(int i=1;i<maxn;++i) //求阶乘的结果及阶乘的逆元的结果
{
fac[i]=(LL)fac[i-1]*i%mod;
infac[i]=(LL)infac[i-1]*qmi(i,mod-2,mod)%mod;
}
}
int main()
{
init();
int n;
cin >> n;
while (n -- )
{
int a,b;
cin >> a >> b;
cout << (LL)fac[a] * infac[b] % mod * infac[a-b] % mod << endl;
}
return 0;
}
887. 求组合数 III
给定 n
组询问,每组询问给定三个整数 a,b,p,其中 p 是质数,请你输出 Cbamodp
的值。
输入格式
第一行包含整数 n
。
接下来 n
行,每行包含一组 a,b,p
。
输出格式
共 n
行,每行输出一个询问的解。
数据范围
1≤n≤20
,
1≤b≤a≤1018,
1≤p≤105
,
输入样例:
3
5 3 7
3 1 5
6 4 13
输出样例:
3
3
2
证明如下:
#include <iostream>
using namespace std;
typedef long long LL;
int qmi(int a,int b,int p)
{
int res=1;
while(b)
{
if(b&1)
res=(LL)res*a%p;
a=(LL)a*a%p;
b>>=1;
}
return res;
}
int CP(int a,int b,int p)
{
int res=1;
for(int i=1;i<=b;++i)
{
res=(LL)res*(a-b+i) % p;
res=(LL)res*qmi(i,p-2,p) % p; //根据费马小定理用逆元求解
}
return res;
}
int Lucas(LL a,LL b,int p)
{
if(a<p&&b<p)
return CP(a,b,p);
else
return (LL)CP(a%p,b%p,p) * Lucas(a/p,b/p,p) % p;
}
int main()
{
int n;
cin >> n;
while (n -- )
{
LL a,b;
int p;
cin >> a >> b >>p;
cout << Lucas(a,b,p) << endl;
}
return 0;
}
将求逆元的过程拿到for循环外面能快一个数量级不止。
如果CP函数这么写,还能快上一倍:
int CP(int a,int b,int p)
{
if(a<b) //用这种方法求结果,一定需要判断a是否小于b,否则得到的答案也许就是错误
return 0;
int mul=a-b; //假设b < a-b
if((a-b) < b) //如果a-b < a,那么还得改变mul和b 的值;(原因是使求阶乘的运算步骤最少)
{
mul=b;
b=a-b;
}
LL x=1,y=1;
for(int i=1;i<=b;++i)
{
x=(mul+i)*x%p; //先分别求分子与分母的阶乘,最后再用逆元求结果
y=y*i%p;
}
return x*qmi(y,p-2,p) %p; //用逆元求结果
}
将求逆元的过程拿到for循环外面:
#include <iostream>
using namespace std;
typedef long long LL;
int qmi(int a,int b,int p)
{
int res=1;
while(b)
{
if(b&1)
res=(LL)res*a%p;
a=(LL)a*a%p;
b>>=1;
}
return res;
}
int CP(int a,int b,int p)
{
LL x=1,y=1;
for(int i=1;i<=b;++i)
{
x=x*(a-b+i) % p;
y=y*i % p;
}
return x*qmi(y,p-2,p)%p;
}
int Lucas(LL a,LL b,int p)
{
if(a<p&&b<p)
return CP(a,b,p);
else
return (LL)CP(a%p,b%p,p) * Lucas(a/p,b/p,p) % p;
}
int main()
{
int n;
cin >> n;
while (n -- )
{
LL a,b;
int p;
cin >> a >> b >>p;
cout << Lucas(a,b,p) << endl;
}
return 0;
}
888. 求组合数 IV
输入 a,b
,求 Cba
的值。
注意结果可能很大,需要使用高精度计算。
输入格式
共一行,包含两个整数 a
和 b
。
输出格式
共一行,输出 Cba
的值。
数据范围
1≤b≤a≤5000
输入样例:
5 3
输出样例:
10
C(a,b) 是个整数,那么我们一定能进行质因数分解;
* 同样,C(a,b)=a!/(b!*(a-b)!),我们可以把分母与分子也进行质因数分解,
* 将分子与分母相同的质因子进行指数相减,最后将所有得到的质因数进行相乘,
* 就能得到最后的结果。
*
* 将分子与分母相同的质因子进行指数相减得到的指数一定是大于等于0的吗?
* 证明:假定存在质因子相减得到的指数小于0的质因子,那么分母一定存在一个
* 质因子,它与任何质因子都是互质的,因此C(a,b) 的结果就以应该是小数,而不是
* 整数,这与C(a,b) 应该为整数矛盾,所以将分子与分母相同的质因子进行指数相减
* 得到的指数一定是大于等于0的,得证!
/**
* C(a,b) 是个整数,那么我们一定能进行质因数分解;
* 同样,C(a,b)=a!/(b!*(a-b)!),我们可以把分母与分子也进行质因数分解,
* 将分子与分母相同的质因子进行指数相减,最后将所有得到的质因数进行相乘,
* 就能得到最后的结果。
*
* 将分子与分母相同的质因子进行指数相减得到的指数一定是大于等于0的吗?
* 证明:假定存在质因子相减得到的指数小于0的质因子,那么分母一定存在一个
* 质因子,它与任何质因子都是互质的,因此C(a,b) 的结果就以应该是小数,而不是
* 整数,这与C(a,b) 应该为整数矛盾,所以将分子与分母相同的质因子进行指数相减
* 得到的指数一定是大于等于0的,得证!
*/
#include <iostream>
#include <vector>
using namespace std;
const int maxn=5010;
int p[maxn] ,num=0 , sum[maxn]; //p存素数,sum素数的指数
bool hs[maxn];
void get_primer(int n); //获得n以内的素数
int cal(int a,int p); //a! 内有多少个 质因子p
void get_pow(int a,int b); //获得C(a,b)内的 质因子的指数
vector<int> mul(vector<int> &A,int b); //高精度与整数的乘法
int main()
{
int a,b;
cin >> a >> b;
get_primer(a);
get_pow(a,b);
vector<int> res;
res.push_back(1);
for(int i=0;i<num;++i)
for(int j=0;j<sum[i];++j)
res=mul(res,p[i]);
for(int i=res.size()-1;i>=0;--i)
cout << res[i];
return 0;
}
void get_primer(int n) //获得n以内的素数
{
for(int i=2;i<=n;++i)
{
if(!hs[i])
p[num++]=i;
for(int j=0;p[j] <= n/i;++j)
{
hs[p[j]*i]=1;
if(i%p[j]==0)
break;
}
}
}
int cal(int a,int p) // a! 内有多少个 质因子p
{
if(a<p)
return 0;
else
return cal(a/p,p) + a/p ;
}
void get_pow(int a,int b) //获得C(a,b)内的 质因子的指数
{
for(int i=0;i<num;++i)
{
int cnt=cal(a,p[i]) - cal(a-b,p[i]) - cal(b,p[i]);
sum[i]=cnt;
}
}
vector<int> mul(vector<int> &A,int b) //高精度与整数的乘法
{
vector<int> res;
int d=0;
for(int i=0;i<A.size();++i)
{
d+=A[i]*b;
res.push_back(d%10);
d/=10;
}
while(d)
{
res.push_back(d%10);
d/=10;
}
return res;
}