递推法 杨辉三角
递推式
C
n
m
=
C
n
−
1
m
+
C
n
−
1
m
−
1
C_{n}^{m} = C_{n-1}^{m} + C_{n-1}^{m-1}
Cnm=Cn−1m+Cn−1m−1
从 n个不同的数中选出 m个的方案数是
C
n
m
C_{n}^{m}
Cnm
对第 1 个数有 选或不选 两种决策:
C
n
m
C_{n}^{m}
Cnm
若不选,则从剩下的 n−1个中选 m 个,即 ;
C
n
−
1
m
C_{n-1}^{m}
Cn−1m
若选,则从剩下的 n−1个中选 m−1 个,即。
C
n
−
1
m
−
1
C_{n-1}^{m-1}
Cn−1m−1
加法原理
C
n
m
=
C
n
−
1
m
+
C
n
−
1
m
−
1
C_{n}^{m} = C_{n-1}^{m} + C_{n-1}^{m-1}
Cnm=Cn−1m+Cn−1m−1
void comb()
{
for(int i=0;i<n;i++)
{
for(int j=0;j<=i;j++)
{
if(j==0)C[i][j] = 1;
else C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
}
}
快速幂+逆元
组合数计算优化方案
递推法
C
n
m
=
C
n
−
1
m
+
C
n
−
1
m
−
1
C_n^m = C_{n-1}^m + C_{n-1}^{m-1}
Cnm=Cn−1m+Cn−1m−1,会 TLE。
考虑用
C
n
m
=
n
!
(
n
−
m
)
!
⋅
m
!
C_n^m = \dfrac{n!}{(n - m)! \cdot m!}
Cnm=(n−m)!⋅m!n! 直接计算。
开两个数组分别存模意义下的阶乘和阶乘的逆元:
- 用 f [ x ] f[x] f[x] 存 x ! m o d p x! \bmod p x!modp 的值;
- 用
g
[
x
]
g[x]
g[x] 存
(
x
!
)
−
1
m
o
d
p
(x!)^{-1} \bmod p
(x!)−1modp 的值。
因为 p p p 是质数且 n , m n, m n,m 都小于 p p p,即 n , m n, m n,m 与 p p p 互质,
所以根据费马小定理 a ⋅ a p − 2 ≡ 1 ( m o d p ) a \cdot a^{p - 2} \equiv 1 \pmod p a⋅ap−2≡1(modp),
因为 a ⋅ a − 1 ≡ 1 ( m o d p ) a \cdot a^{-1} \equiv 1 \pmod p a⋅a−1≡1(modp)
所以 a − 1 ≡ a p − 2 ( m o d p ) a^{-1} \equiv a^{p-2} \pmod p a−1≡ap−2(modp)
可以用快速幂求逆元。
无法直接计算 n ! ( n − m ) ! ⋅ m ! m o d p \dfrac{n!}{(n - m)! \cdot m!}\bmod p (n−m)!⋅m!n!modp,所以转化为 n ! ⋅ ( ( n − m ) ! ⋅ m ! ) − 1 m o d p n!\cdot((n-m)!\cdot m!)^{-1} \bmod p n!⋅((n−m)!⋅m!)−1modp。
所以 C n m m o d p = f [ n ] × g [ n − m ] × g [ m ] m o d p C_n^m \bmod p = f[n] \times g[n - m] \times g[m] \bmod p Cnmmodp=f[n]×g[n−m]×g[m]modp。
typedef long long ll;
ll qpow(ll a,int b,ll p)
{
ll res;
while(b)
{
if(a & 1)res=res*a%p;
a=a*a%p;
b >>= 1;
}
return res;
}
void init()
{
f[0]=1,g[0]=1;
for(int i=1;i<n;i++)
{
f[i] = i*f[i-1]%p;
g[i] = g[i-1]*qpow(i,p-2)%p;
}
}
ll comb(ll n,ll m)
{
return f[n]*g[n-m]%p*g[m]%p;
}
卢卡斯定理
卢卡斯定理:
C
n
m
≡
C
n
/
p
m
/
p
⋅
C
n
m
o
d
p
m
m
o
d
p
(
m
o
d
p
)
C_n^m \equiv C_{n/p}^{m/p} \cdot C_{n \bmod p}^{m \bmod p} \pmod{p}
Cnm≡Cn/pm/p⋅Cnmodpmmodp(modp)
其中
p
p
p 为质数。
n
m
o
d
p
n \bmod p
nmodp 和
m
m
o
d
p
m \bmod p
mmodp 一定是小于
p
p
p 的数,可以直接求解,
C
n
/
p
m
/
p
C_{n/p}^{m/p}
Cn/pm/p 可以继续用 Lucas 定理求解。
边界条件:当
m
=
0
m = 0
m=0 时,返回
1
1
1。
typedef long long ll;
ll qpow(ll a,int b,ll p)
{
ll res;
while(b)
{
if(a & 1)res=res*a%p;
a=a*a%p;
b >>= 1;
}
return res;
}
void init(ll N,ll p)
{
f[0]=1,g[0]=1;
for(int i=1;i<N;i++)
{
f[i] = i*f[i-1]%p;
g[i] = g[i-1]*qpow(i,p-2)%p;
}
}
ll comb(ll n,ll m,ll p)
{
return f[n]*g[n-m]%p*g[m]%p;
}
ll lucas(ll n,ll m)
{
if(m==0)return 1;
return lucas(n/p,m/p)*comb(n%p,m%p)%p;
}