保证是你见过最简洁明了的排列组合文章
费马小定理
如果p是一个质数,而整数a不是p的倍数,则有
a
p
−
1
≡
1
a^{p-1}≡1
ap−1≡1 (mod p)
证明:略
乘法逆元
简单的可以理解成一个数 对p取模 的倒数
证明:
若b、m互质,并且b | a,则存在一个整数x,使得a / b ≡ a * x (mod m),则称x为b的模m乘法逆元,记为 b − 1 b^{-1} b−1(mod m)。
因为 a / b = a ∗ b − 1 ≡ a / b ∗ b ∗ b − 1 a / b = a * b^{-1}≡ a/b * b * b^{-1} a/b=a∗b−1≡a/b∗b∗b−1,所以 b ∗ b − 1 ≡ 1 b * b^{-1} ≡1 b∗b−1≡1(mod m)
如果m是质数,(后文用p代表m)并且b < p,根据费马小定理
b
p
−
1
≡
1
b^{p-1}≡1
bp−1≡1(mod p),
即
b
∗
b
−
2
≡
1
b* b^{-2} ≡ 1
b∗b−2≡1(mod p)。
因此,当模数p为质数的时候,
b
p
−
2
b^{p-2}
bp−2即为b的乘法逆元
结论:
- 当模数p与b互质的时候, b p − 2 b^{p-2} bp−2即为b的乘法逆元
- 通常用于除法中求模
如果p不是素数的情况,需要求逆元:
a
/
b
a / b
a/b (mod m) =
x
/
d
x/d
x/d(mod
d
∗
m
d* m
d∗m)
组合的简单的写法
看别人的没看明白,顺着自己思路写了
也没什么好讲的,就是公式的写法。
组合的公式: C n m = n ! m ! ( n − m ) ! C^m_n = \frac{n!}{ m!(n-m)!} Cnm=m!(n−m)!n!
根据上文的逆元
公式可以改写成:
C
n
m
=
n
!
m
!
(
n
−
m
)
!
=
n
!
∗
i
n
v
[
m
]
%
m
o
d
∗
i
n
v
[
n
−
m
]
%
m
o
d
C^m_n =\frac{n!}{m!(n-m)!} = n! * inv[m]\%mod * inv[n-m]\%mod
Cnm=m!(n−m)!n!=n!∗inv[m]%mod∗inv[n−m]%mod
此处inv[m]已经是m阶乘的inv
问题如下:
给定n,m 求
C
n
m
C^m_n
Cnm,结果对
1
0
9
+
7
10^9+7
109+7取模
思路是打表 c[n] = n!,以及对应的逆元inv[n]。然后输出结果
#include <iostream>
#define ll long long
using namespace std;
const int N = 1e5+5,mod= 1e9+7;
int a,b,t;
ll c[N], inv[N];
ll qpow(ll a,int b){
ll ans = 1;
while(b){
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
inline ll C(ll a,ll b,ll p){
return c[a] * inv[b] % p * inv[a-b] % p;
// a / b % mod == a * inv[b] % mod;
}
int main(){
c[0] = 1;
for(int i=1;i<=N;i++) c[i] = c[i-1] * i % mod;// 求排列数
inv[N-1] = qpow(c[N-1], mod - 2);// 求排列数的逆元 b的逆元就是 b^(mod-2)
for(int i = N-2;i >= 0;i--) inv[i] = inv[i + 1] * (i + 1) % mod;
cin >> t;
while(t--){
cin >> a >> b;
cout << C(a, b, mod) << "\n";
}
return 0;
}
Lucas定理
求值
C
a
b
C^b_a
Cab mod p
适用于:数论中求大组合数取模.
比如:
1 ≤ b ≤ a ≤
1
0
18
10^{18}
1018
1≤ p ≤
1
0
5
10^5
105
Lucas定理完整内容:
C
n
m
=
∏
i
=
0
k
C
n
i
m
i
C_n^m = \prod\limits^{k}_{i=0}C^{m_i}_{n_i}
Cnm=i=0∏kCnimi(mod p)
n
=
n
k
p
k
+
n
k
−
1
p
k
−
1
+
.
.
.
+
n
1
p
+
n
0
n = n_kp^k+ n_{k-1}p^{k-1}+...+n_1p+n_0
n=nkpk+nk−1pk−1+...+n1p+n0
m
=
m
k
p
k
+
m
k
−
1
p
k
−
1
+
.
.
.
+
m
1
p
+
m
0
m = m_kp^k+m_{k-1}p^{k-1}+...+m_1p+m_0
m=mkpk+mk−1pk−1+...+m1p+m0
简易写法:
C
a
b
≡
C
a
%
p
b
%
p
∗
C
a
/
p
b
/
p
%
p
C^b_a≡ C^{b\%p}_{a\%p} * C^{b/p}_{a/p} \%p
Cab≡Ca%pb%p∗Ca/pb/p%p
其中 C a / p b / p C^{b/p}_{a/p} Ca/pb/p可以递归
那说起递归可能你比我还懂啊。
代码
ll lucas(ll a, ll b, ll p){
if(a < p && b < p) return C(a, b, p);
return C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}