文章目录
质数
- 又称为素数
1 - 定义
- 在大于 1 1 1 的整数中,只包含 1 1 1 和本身这两个约数的数
2 - 判定
试除法,根据定义来判定
bool is_prime(int n) {
for(int i=2;i<n;i++) {
if(x % i == 0) return 0;
}
return 1;
}
优化:
- 首先,我们可以由 d ∣ n d\mid n d∣n 推导 n d ∣ n \frac{n}{d} \mid n dn∣n
- 所以我们在枚举约数时只需要枚举 d ≤ n d d \le \frac{n}{d} d≤dn 的部分,即 d ≤ n d \le \sqrt n d≤n
bool is_prime(int n) {
for(int i=2;i<=n/i;i++) {
if(n % i == 0) return 0;
}
return 1;
}
时间复杂度由 O ( n ) O(n) O(n) 降为 O ( n ) O(\sqrt n) O(n)
3 - 分解质因数
试除法,从小到大枚举 n n n 的所有因数
void divide(int n) {
for(int i=2;i<=n;i++)
if(n % i == 0) {
int cnt = 0;
while(n % i == 0) {
n /= i;
cnt++;
}
cout << i << " " << cnt << endl; // 输出底数和指数
}
}
}
优化:
- n n n 当中最多只包含一个大于 n \sqrt n n 的质因子
- 枚举时先举出小于等于 n \sqrt n n 的质因子,最后大于 n \sqrt n n 的质因子特判即可
void divide(int n) {
for(int i=2;i<=n;i++)
if(n % i == 0) {
int cnt = 0;
while(n % i == 0) {
n /= i;
cnt++;
}
cout << i << " " << cnt << endl;
}
}
if(n > 1) cout << n << " " << 1 << endl; // 特判
}
时间复杂度由 O ( n ) O(n) O(n) 降为 O ( n ) O(\sqrt n) O(n)
4 - 筛质数
即把质数从一定的范围内快速的选出
埃氏筛
在 2 2 2 ~ n n n 中筛质数,每次删除 2 、 3 … n − 1 2、3 … n-1 2、3…n−1 的倍数,剩下的为质数
原理:
- 对于 p i p_i pi ,若它在第 i − 1 i-1 i−1 轮没有被删掉,说明 2 、 3 … n − 1 2、3…n-1 2、3…n−1 都不是 p i p_i pi 的因数,即 p i p_i pi 只有 1 1 1 和 n n n 这两个因数
void get_primes(int n) {
for(int i=2;i<=n;i++) {
if(!t[i]) {
primes[ans++] = i; // 记录质数
}
for(int j=i+i;j<=n;j+=i) t[j] = 1; // 删掉 i 的倍数
}
}
优化:
- 合数是质数的倍数,所以合数的倍数不用再删一遍
void get_primes(int n) {
for(int i=2;i<=n;i++) {
if(!t[i]) {
primes[ans++] = i;
for(int j=i+i;j<=n;j+=i) t[j] = 1;
}
}
}
时间复杂度由 O ( n log n ) O(n \log n) O(nlogn) 降为 O ( n log log n ) O(n\log \log n) O(nloglogn) ,几乎为 O ( n ) O(n) O(n)
线性筛
原理:
-
用每个合数的最小质因子筛掉它
-
遍历质数列表,依次筛掉 p j p_j pj 的 i i i 倍
-
i m o d j = 0 i\mod j=0 imodj=0 ,因为是从小到大遍历的,所以 p j p_j pj 一定是 i i i 的最小质因子,也是 p j × i p_j \times i pj×i 的最小质因子
-
i m o d j ≠ 0 i \mod j \neq 0 imodj=0,同上, p j p_j pj 一定小于 i i i 的所有质因子, 也是 p j × i p_j \times i pj×i 的最小质因子
-
void get_primes(int n) {
for(int i=2;i<=n;i++) {
if(!t[i]) primes[ans++] = i;
for(int j=0;primes[j]<=n/i;j++) { // 枚举每个质数
t[primes[j] * i] = 1; // 筛,见上
if(i % primes[j] == 0) break;
}
}
}
时间复杂度为 O ( n ) O(n) O(n) ,比埃筛稍快
约数
1 - 求约数
试除法
优化:
- 由 d ∣ n d \mid n d∣n 推导 n d ∣ n \frac{n}{d} \mid n dn∣n
- 所以枚举较小的约数,另一个直接算
- 枚举 d ≤ n d d \le \frac{n}{d} d≤dn ,即 d ≤ n d \le \sqrt{n} d≤n
void get_divisors(int n) {
for(int i=1;i<=n/i;i++) {
if(n % i == 0) {
ans[++cnt] = i;
if(i != n / i) ans[++cnt] = n / i;
}
}
}
时间复杂度为 O ( n ) O(\sqrt n) O(n)
2 - 约数个数
用约数个数定理
- 对于一个大于 1 1 1 的正整数 n n n ,可分解为: p 1 a 1 ⋅ p 2 a 2 … p k a k {p_1}^{a_1}\cdot {p_2}^{a_2}…{p_k}^{a_k} p1a1⋅p2a2…pkak
- 而 p i a i {p_i}^{a_i} piai 有约数 p i 0 , p i 1 , p i 2 , … p i a i {p_i}^0,{p_i}^1,{p_i}^2,…{p_i}^{a_i} pi0,pi1,pi2,…piai ,共 a i + 1 a_i+1 ai+1 个
- 所以 n n n 共有 ( a 1 + 1 ) ( a 2 + 1 ) … ( a i + 1 ) (a_1+1)(a_2+1)…(a_i+1) (a1+1)(a2+1)…(ai+1) 个约数
同余
- 如果两个数 a a a 和 b b b 之差能被 m m m 整除,那么我们就称 a a a 和 b b b 对模数 m m m 同余
- 记作 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm)
1 - 性质
- 自反性(一个数永远和自己同余)
- 对称性( a a a 和 b b b 同余, b b b 和 a a a 也就同余)
- 传递性( a a a 和 b b b 同余, b b b 和 c c c 同余可以推出 a a a 和 c c c 同余)
2 - 推论
- 如果 a ≡ b ( m o d m ) , x ≡ y ( m o d m ) a\equiv b\pmod m,x\equiv y\pmod m a≡b(modm),x≡y(modm),则 a ± x ≡ b ± y ( m o d m ) a\pm x\equiv b\pm y\pmod m a±x≡b±y(modm)
- 如果 a ≡ b ( m o d m ) , x ≡ y ( m o d m ) a\equiv b\pmod m,x\equiv y\pmod m a≡b(modm),x≡y(modm),则 a x ≡ b y ( m o d m ) ax\equiv by\pmod m ax≡by(modm)
- 如果 a c ≡ b c ( m o d m ) ac\equiv bc\pmod m ac≡bc(modm),且 c c c 和 m m m 互质,则 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm)
- 如果 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm),且 c ≠ 0 c\ne 0 c=0,则 a c ≡ b c ( m o d m c ) ac\equiv bc\pmod {mc} ac≡bc(modmc)
费马小定理
如果
p
p
p 是质数,且
a
,
p
a,p
a,p 互质,有:
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1}\equiv1\pmod p
ap−1≡1(modp)
应用
求乘法逆元
若 ( x × y ) ≡ 1 ( m o d p ) (x\times y)\equiv 1\pmod p (x×y)≡1(modp) ,则 y y y 为 x x x 模 p p p 的乘法逆元
-
因为 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1\pmod p ap−1≡1(modp) ,所以 ( a × a p − 2 ) ≡ 1 ( m o d p ) (a\times a^{p-2})\equiv 1\pmod p (a×ap−2)≡1(modp)
-
即 a p − 2 a^{p-2} ap−2 是 a a a 关于模 p p p 的乘法逆元
a b m o d p = ( a m o d p ) ( b ′ m o d p ) m o d p \frac{a}{b}\mod p=(a\mod p)(b'\mod p)\mod p bamodp=(amodp)(b′modp)modp
其中 p p p 为质数, b ′ b' b′ 为 b b b 的乘法逆元
欧拉函数
-
对于一个正整数 x x x ,小于 x x x 且和 x x x 互质的正整数的个数表示为 φ ( x ) \varphi (x) φ(x)
-
φ ( 1 ) \varphi (1) φ(1) 的值为 1 1 1 ,没有实际意义
1 - 通式
-
φ ( x ) = x ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) … ( 1 − 1 p n ) \varphi (x)=x(1-\frac{1}{p_1})(1-\frac{1}{p_2})…(1-\frac{1}{p_n}) φ(x)=x(1−p11)(1−p21)…(1−pn1)
其中, p 1 , p 2 … , p n p_1,p_2…,p_n p1,p2…,pn 是 x x x 的所有质因数
-
φ ( x ) = p k − p k − 1 \varphi (x)=p^k-p^{k-1} φ(x)=pk−pk−1
其中, x = p k x=p^k x=pk 且 p p p 为质数
2 - 性质
-
欧拉函数是积性函数
若 x , y x,y x,y 互质,则 φ ( x × y ) = φ ( x ) × φ ( y ) \varphi(x\times y)=\varphi(x)\times \varphi(y) φ(x×y)=φ(x)×φ(y)
-
若 x x x 是质数,则 φ ( x ) = x − 1 \varphi(x)=x-1 φ(x)=x−1
欧拉定理
若
a
,
n
a,n
a,n 为正整数,且
a
,
n
a,n
a,n 互质,有:
a
φ
(
n
)
≡
1
(
m
o
d
n
)
a^{\varphi(n)}\equiv 1\pmod n
aφ(n)≡1(modn)
1 - 应用
降幂,当
a
,
n
a,n
a,n 互质时:
a
b
m
o
d
n
=
a
b
m
o
d
φ
(
n
)
m
o
d
n
a^b\mod n=a^{b\mod \varphi(n)}\mod n
abmodn=abmodφ(n)modn
2 - 扩欧
当
b
≥
φ
(
c
)
b\ge \varphi(c)
b≥φ(c) 时:
a
b
m
o
d
c
=
a
b
m
o
d
φ
(
c
)
+
φ
(
c
)
m
o
d
c
a^b\mod c=a^{b\mod \varphi(c)+\varphi(c)}\mod c
abmodc=abmodφ(c)+φ(c)modc
3 - 代码
-
求 φ ( x ) \varphi (x) φ(x)
int phi(int x) { int ans = x; for(int i=2;i<=x/i;i++) { if(x % i == 0) { ans -= ans/i; while(x % i == 0) x /= i; } } if(x > 1) ans -= ans / x; return ans; }
时间复杂度 O ( n ) O(\sqrt n) O(n)
-
求 φ ( 1 ) , φ ( 2 ) … , φ ( n ) \varphi(1),\varphi(2)…,\varphi(n) φ(1),φ(2)…,φ(n)
void phi(int n) { for(int i=1;i<=n;i++) p[i] = i; for(int i=2;i<=n;i++) { if(p[i] == i) { for(int j=i;j<=n;j+=i) { p[j] = p[j] / i * (i - 1); } } } }
时间复杂度 O ( n log log n ) O(n\log\log n) O(nloglogn)
扩展欧几里得算法
在已知整数 a , b a,b a,b 的情况下,求 a x + b y = gcd ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 的整数解
1 - 解法
-
该方程一定有解
-
x = y ′ y = x ′ − a b × y x=y'\\ y=x'-\frac{a}{b}\times y x=y′y=x′−ba×y
到最后可得 x ′ = 1 , y ′ = 0 x'=1,y'=0 x′=1,y′=0,于是逐步往前推即可
2 - 代码
int k_ou(int a,int b,int &x1,int &y1) {
if(b == 0) {
x1 = 1,y1 = 0;
return a;
}
int x2,y2;
int d = k_ou(b,a % b,x2,y2);
x1 = x2,y1 = x2 - a / b * y2;
return d;
}
int k_ou(int a,int b,int &x,int &y) {
if(b == 0) {
x = 1,y = 0;
return a;
}
int d = k_ou(b,a % b,x,y);
int temp = x;
x = y,y = temp - a / b * y;
return d;
}
3 - 应用
不定方程
-
对于不定方程 a x + b y = c ax+by=c ax+by=c ,扩欧算法可以判断是否有解,并求出通解
-
如果 c m o d d ≠ 0 c\mod d\ne0 cmodd=0 那么该方程无解(其中 d d d 可为 gcd ( a , b ) \gcd(a,b) gcd(a,b))
-
当 c m o d d = 0 c\mod d=0 cmodd=0 时,即该方程有解时:
-
设 d = gcd ( a , b ) d=\gcd(a,b) d=gcd(a,b) ,因此可用扩欧求出 a x ′ + b y ′ = d = gcd ( a , b ) ax'+by'=d=\gcd(a,b) ax′+by′=d=gcd(a,b) 的解 x ′ , y ′ x',y' x′,y′
-
则原方程的一组解 x = x ′ × c d , y = y ′ × c d x=x'\times \frac{c}{d},y=y'\times \frac{c}{d} x=x′×dc,y=y′×dc
-
而所有的解可表示为:
x = x ′ × c d + k × b d y = y ′ × c d − k × a d x=x'\times \frac{c}{d}+k\times \frac{b}{d}\\ y=y'\times \frac{c}{d}-k\times \frac{a}{d} x=x′×dc+k×dby=y′×dc−k×da
其中 k k k 为任意整数
-
模线性方程
- 解形如 a x ≡ b ( m o d n ) ax\equiv b\pmod n ax≡b(modn) 的方程
- 根据模的定义,可得 a x − b = n k ax-b=nk ax−b=nk , k k k 为整数
- 移项得: a x − n k = b ax-nk=b ax−nk=b ,可用扩欧解决
中国剩余定理
-
设 w 1 , w 2 … , w n w_1,w_2…,w_n w1,w2…,wn 是两两互质的正整数
有方程:
{ x ≡ b 1 ( m o d w 1 ) x ≡ b 2 ( m o d w 2 ) … x ≡ b n ( m o d w n ) \begin{equation} \left\{ \begin{array}{lr} x\equiv b_1\pmod{w_1}\\ x\equiv b_2\pmod{w_2}\\ …\\ x\equiv b_n\pmod{w_n}\\ \end{array} \right. \end{equation} ⎩ ⎨ ⎧x≡b1(modw1)x≡b2(modw2)…x≡bn(modwn) -
上面方程的解为
x = ( m 1 × m 1 − 1 × b 1 + m 2 × m 2 − 1 … + m n × m n − 1 × b n ) m o d p x=(m_1\times m_1^{-1}\times b_1+m_2\times m_2^{-1}…+m_n\times m_n^{-1}\times b_n)\mod p x=(m1×m1−1×b1+m2×m2−1…+mn×mn−1×bn)modp -
其中, p = w 1 × w 2 … × w n p=w_1\times w_2…\times w_n p=w1×w2…×wn
m i = p w i m_i=\frac{p}{w_i} mi=wip
m i − 1 m_i^{-1} mi−1 是 m i m_i mi 模 w i w_i wi 的乘法逆元,即 m i × m i − 1 ≡ 1 ( m o d w i ) m_i \times m_i^{-1}\equiv 1\pmod {w_i} mi×mi−1≡1(modwi)
-
怎么求乘法逆元?
m i × m i − 1 − w i × k = 1 m_i\times m_i^{-1}-w_i\times k=1 mi×mi−1−wi×k=1
扩欧求解即可
代码
int china(int b[],int w[],int n) {
int ans = 0,p = 1;
for(int i=1;i<=n;i++) p *= w[i];
for(int i=1;i<=n;i++) {
int mi = p / w[i];
int d = k_ou(mi,w[i],x,y);
ans = (ans + x * mi * b[i]) % p;
}
if(ans > 0) return ans;
else return ans + p;
}
组合数学
1 - 排列组合
-
从 n n n 个不同的元素中,取 m m m 个不重复的元素,按次序排列,即为排列
A n m = n ! ( n − m ) ! A_n^m=\frac{n!}{(n-m)!} Anm=(n−m)!n! -
从 n n n 个不同的元素中,取 m m m 个不重复的元素,不考虑顺序,即为组合
C n m = n ! ( n − m ) ! m ! C_n^m=\frac{n!}{(n-m)!\ m!} Cnm=(n−m)! m!n! -
组合数的性质:
C n m = C n n − m C_n^m=C_n^{n-m} Cnm=Cnn−mC 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
C n 0 + C n 1 … + C n n = 2 n C_n^0+C_n^1…+C_n^n=2^n Cn0+Cn1…+Cnn=2n
斯特林数
1 - 第一类斯特林数
-
用 s 1 n , m s1_{n,m} s1n,m 表示把 n n n 个元素划分为 m m m 个非空循环排列集合的方案数
-
S 1 n , m = S 1 n − 1 , m − 1 + ( n − 1 ) × S 1 n − 1 , m S_{1\ n,m}=S_{1\ n-1,m-1}+(n-1)\times S_{1\ n-1,m} S1 n,m=S1 n−1,m−1+(n−1)×S1 n−1,m
void get() {
for(int i=0;i<=n;i++) s1[i][i] = 1;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m&&j<=i;j++) {
s1[i][j] = (s[i - 1][j - 1] + (i - 1) * s1[i - 1][j]) % mod;
}
}
}
2 - 第二类斯特林数
-
用 s 2 n , m s2_{n,m} s2n,m 来表示把 n n n 个元素划分成 m m m 个非空集合的方案数
-
S 2 n , m = S 2 n − 1 , m − 1 + m × S 2 n − 1 , m S_{2\ n,m}=S_{2\ n-1,m-1}+m\times S_{2\ n-1,m} S2 n,m=S2 n−1,m−1+m×S2 n−1,m
void get() {
for(int i=1;i<=n;i++) s2[i][1] = 1;
for(int i=1;i<=n;i++) {
for(int j=2;j<=i&&j<=m;j++) {
s2[i][j] = (s2[i - 1][j - 1] + j * s2[i - 1][j]) % mod;
}
}
}
3 - BELL数
-
BELL 数 b n b_n bn 表示把 n n n 个元素划分为若干个非空集合的方案数
-
b n = S 2 n , 1 + S 2 , n , 2 … + S 2 n , n b_n=S_{2\ n,1}+S_{2,\ n,2}…+S_{2\ n,n} bn=S2 n,1+S2, n,2…+S2 n,n
Lucas 定理
-
计算 C n m m o d p C_n^m\mod p Cnmmodp , p p p 是质数
-
L u c a s ( n , m , p ) = C n m o d p m m o d p × L u c a s ( a p , b p , p ) Lucas(n,m,p)=C_{n\mod p}^{m\mod p}\times Lucas(\frac{a}{p},\frac{b}{p},p) Lucas(n,m,p)=Cnmodpmmodp×Lucas(pa,pb,p)
代码
ll ksm(ll a,ll b,ll p) {
ll ans = 1;
while(b) {
if(b & 1) ans = (ans * a) % p;
a = (a * a) % p;
b >>= 1;
}
return ans;
}
ll c(ll a,ll b) {
if(a < b) return 0;
if(a == b) return 1;
if(b > a - b) b = a - b;
ll A = 1,B = 1;
for(ll i=0;i<b;i++) {
A = (A * (a - i)) % p;
B = (B * (b - i)) % p;
}
return (a * ksm(B,p - 2,p)) % p;
}
ll Lucas(ll n,ll m) {
if(m == 0) return 1;
return c(n % p,m % p) * Lucas(n / p,m / p) % p;
}