求组合数I(暴力做法) O ( n ) O(n) O(n)
公 式 : C a b = C a − 1 b − 1 + C a − 1 b 公式:C_{a}^{b}=C_{a-1}^{b-1}+C_{a-1}^{b} 公式:Cab=Ca−1b−1+Ca−1b
做法类似杨辉三角
模板题:AcWing 885. 求组合数 I
注意:当b等于0或者a==b的时候直接赋值为1
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2010,mod=1e9+7;
int n,a,b,arr[MAXN][MAXN];
int main()
{
for(int i=1;i<MAXN;++i)
for(int j=0;j<=i;++j)
{
if(j==0||j==i)
arr[i][j]=1;
else
arr[i][j]=(arr[i-1][j-1]+arr[i-1][j])%mod;
}
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&a,&b);
printf("%d\n",arr[a][b]);
}
return 0;
}
求组合数II(逆元, O ( n l o g n ) O(nlogn) O(nlogn)
C
a
b
=
a
!
(
a
−
b
)
!
b
!
C_{a}^{b}=\frac{a!}{(a-b)!b!}
Cab=(a−b)!b!a!
用
f
a
c
t
[
a
]
fact[a]
fact[a]数组存储
a
\,a
a的阶乘
a
!
\,a!
a!,
i
n
f
a
c
t
[
a
]
infact[a]
infact[a]存储
a
!
\,a!
a!的逆元,用快速幂来求
那么
C
a
b
=
f
a
c
t
[
a
]
∗
i
n
f
a
c
t
[
a
−
b
]
%
m
o
d
∗
i
n
f
a
c
t
[
b
]
%
m
o
d
C_{a}^{b}=fact[a]\,\,\,*\,\,\,infact[a-b]\%mod\,\,\,*\,\,\,infact[b]\%mod
Cab=fact[a]∗infact[a−b]%mod∗infact[b]%mod
需要注意的是
i
n
f
a
c
t
[
i
]
=
i
n
f
a
c
t
[
i
−
1
]
∗
q
m
i
(
i
,
m
o
d
−
2
,
m
o
d
)
%
m
o
d
\,\,\,infact[i] = infact[i - 1]\,\,\, *\,\,\, qmi(i, mod - 2, mod) \% mod
infact[i]=infact[i−1]∗qmi(i,mod−2,mod)%mod
前面是
1
(
i
−
1
)
!
\frac{1}{(i-1)!}
(i−1)!1,后面是
1
i
\frac{1}{i}
i1,乘起来就是
1
i
!
\frac{1}{i!}
i!1
模板题:AcWing 886. 求组合数 II
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN=1e5+10,mod=1e9+7;
ll fact[MAXN],infact[MAXN],n,a,b;
ll qmi(ll a,ll k,ll p)
{
ll res=1;
while(k)
{
if(k&1)
res=res*a%p;
a=a*a%p;
k>>=1;
}
return res;
}
int main()
{
fact[0]=infact[0]=1;
for(int i=1;i<MAXN;++i)
{
fact[i]=fact[i-1]*i%mod;
infact[i]=infact[i-1]*qmi(i,mod-2,mod)%mod;
}
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&a,&b);
printf("%d\n",fact[a]*infact[a-b]%mod*infact[b]%mod);
}
return 0;
}
求组合数III(卢卡斯定理)
C
a
b
=
C
a
m
o
d
p
b
m
o
d
p
+
C
a
/
p
b
/
p
(
m
o
d
p
)
,
a
/
p
和
b
/
p
表
示
整
除
,
证
明
有
点
复
杂
,
就
不
放
在
这
里
了
C_{a}^{b}=C_{a\,\,mod\,p}^{b\,\,mod\,p}+C_{a/p}^{b/p}\,\,\,(mod\,p),a/p和b/p表示整除,证明有点复杂,就不放在这里了
Cab=Camodpbmodp+Ca/pb/p(modp),a/p和b/p表示整除,证明有点复杂,就不放在这里了
模板题:AcWing 887. 求组合数 III
注意传进
l
u
c
a
s
lucas
lucas 里面需要是
l
l
ll
ll 类型
代码如下:
#include<iostream>
using namespace std;
#define ll long long
int p;
ll qmi(ll a, ll k, ll p)
{
ll res = 1;
while(k)
{
if(k & 1)
res = res * a % p;
a = a * a % p;
k >>= 1;
}
return res;
}
ll C(ll a, ll b)
{
ll res = 1;
for(ll i = 1, j = a; i <= b; ++i, --j)
{
res = res * j % p;
res = res * qmi(i, p - 2, p) % p;
}
return res;
}
ll lucas(ll a, ll b)
{
if(a < p && b < p)
return C(a, b);
return C(a % p, b % p) * lucas(a / p, b / p) % p;
}
int main()
{
int n;
scanf("%d", &n);
while(n--)
{
ll a, b;
scanf("%lld%lld%lld", &a, &b, &p);
printf("%lld\n", lucas(a, b));
}
return 0;
}
求组合数IV
输入 a , b a,b a,b,求 C a b C_{a}^{b} Cab 的值,不模,用高精度
1 ≤ b ≤ a ≤ 5000 1≤b≤a≤5000 1≤b≤a≤5000
C
a
b
=
a
!
(
a
−
b
)
!
b
!
=
p
1
a
1
∗
p
2
a
2
∗
⋅
⋅
⋅
∗
p
k
a
k
←
分
解
质
因
数
C_{a}^{b}=\frac{a!}{(a-b)!b!}=p_1^{a1}*p_2^{a2}*···*p_k^{a_k}←分解质因数
Cab=(a−b)!b!a!=p1a1∗p2a2∗⋅⋅⋅∗pkak←分解质因数
把分子中
a
!
\,a!
a! 里面的含有
p
\,p
p的个数求出来,再求出分子的,再相减
s
u
m
[
i
]
表
示
a
的
阶
乘
里
面
有
多
少
个
第
i
个
质
数
,
质
数
从
2
开
始
算
sum[i]表示\,a\,的阶乘里面有多少个第i个质数,质数从2开始算
sum[i]表示a的阶乘里面有多少个第i个质数,质数从2开始算
s
u
m
[
i
]
=
a
p
+
a
p
2
+
a
p
3
+
⋅
⋅
⋅
sum[i]=\frac{a}{p}+\frac{a}{p^2}+\frac{a}{p^3}+···
sum[i]=pa+p2a+p3a+⋅⋅⋅
比如
8
!
=
1
∗
2
∗
3
∗
4
∗
5
∗
6
∗
7
∗
8
8!=1*2*3*4*5*6*7*8
8!=1∗2∗3∗4∗5∗6∗7∗8
s
u
m
[
1
]
=
8
/
2
+
8
/
4
+
8
/
8
=
4
+
2
+
1
=
7
,
表
示
第
一
个
质
数
2
在
8
的
阶
乘
里
面
有
7
个
,
第
二
个
质
数
是
3
⋅
⋅
⋅
等
等
sum[1]=8/2+8/4+8/8=4+2+1=7,表示第一个质数2在8的阶乘里面有7个,第二个质数是3···等等
sum[1]=8/2+8/4+8/8=4+2+1=7,表示第一个质数2在8的阶乘里面有7个,第二个质数是3⋅⋅⋅等等
模板题如下:AcWing 888. 求组合数 IV
步骤如下:
①:先线性筛预处理出来小于a的所有质数
void get_primes(int n)
{
for(int i = 2; i <= n; ++i)
{
if(!st[i])
primes[cnt++] = i;
for(int j = 0; primes[j] <= n / i; ++j)
{
st[primes[j] * i] = true;
if(i % primes[j] == 0)
break;
}
}
}
②:遍历得到组合数的结果里面所有质数的个数, g e t get get 函数也要注意有技巧,注意 s u m [ i ] sum[i] sum[i] 表示第 i i i 个质数的个数
int get(int a, int p)
{
int res = 0;
while(a)
{
res += a / p;
a /= p;
}
return res;
}
int main()
{
for(int j = 0; j < cnt; ++j)
{
int p = primes[j];//这里相当于离散化了
sum[j] = get(a, p) - get(a - b, p) - get(b, p);
}
}
③:对于结果 v e c t o r < i n t > r e s vector<int>res vector<int>res,首先先 p u s h _ b a c k push\_back push_back一个 1 1 1,然后在分别处理每个质数的与它的个数,这里是大数相乘,最后倒序输出
完整代码如下:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5010;
int primes[MAXN], st[MAXN], cnt, sum[MAXN];
void get_primes(int n)
{
for(int i = 2; i <= n; ++i)
{
if(!st[i])
primes[cnt++] = i;
for(int j = 0; primes[j] <= n / i; ++j)
{
st[primes[j] * i] = true;
if(i % primes[j] == 0)
break;
}
}
}
int get(int a, int p)
{
int res = 0;
while(a)
{
res += a / p;
a /= p;
}
return res;
}
vector<int> mul(vector<int> a, int b)
{
vector<int> c;
int t = 0;
for(int i = 0; i < a.size(); ++i)
{
t += a[i] * b;
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 j = 0; j < cnt; ++j)
{
int p = primes[j];//这里相当于离散化了
sum[j] = get(a, p) - get(a - b, p) - get(b, p);
}
vector<int> res;
res.push_back(1);
for(int j = 0; j < cnt; ++j)
for(int i = 1; i <= sum[j]; ++i)
res = mul(res, primes[j]);
for(int i = res.size() - 1; i >= 0; --i)
printf("%d", res[i]);
return 0;
}