ACM 数学知识整理
一、素数
素数的定义:因数只有1和它本身的数
(1) 线性筛
int primes[N], cnt;
bool st[N];
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] = 1;
if(i % primes[j] == 0) break;
}
}
}
二、约数
(1) 分解质因数
对于一个大于1的整数n,n可以分解质因数为:
n
=
∏
i
=
1
k
p
i
a
i
=
p
1
a
1
⋅
p
2
a
2
⋅
⋅
⋅
p
k
a
k
n=\prod_{i=1}^{k}{p_i^{a_i}}=p_1^{a_1}\cdot p_2^{a_2}\cdot\cdot\cdot p_k^{a_k}
n=i=1∏kpiai=p1a1⋅p2a2⋅⋅⋅pkak
试除法分解质因数:
for(int i = 2; i * i <= a; ++i)
{
if(a % i == 0)
{
int s = 0; //s为约数i的指数
while(a % i == 0)
{
a /= i;
s++;
}
//对i^s该项处理
......
}
}
if(a > 1)
{
//说明本身含有一个大于根号a的质因子
......
}
(2) 约数个数公式
根据分解质因数所得式:
n
=
∏
i
=
1
k
p
i
a
i
=
p
1
a
1
⋅
p
2
a
2
⋅
⋅
⋅
p
k
a
k
n=\prod_{i=1}^{k}{p_i^{a_i}}=p_1^{a_1}\cdot p_2^{a_2}\cdot\cdot\cdot p_k^{a_k}
n=i=1∏kpiai=p1a1⋅p2a2⋅⋅⋅pkak
则n的约数个数为:
f
(
n
)
=
∏
i
=
1
k
(
a
i
+
1
)
=
(
a
1
+
1
)
(
a
2
+
1
)
⋅
⋅
⋅
(
a
k
+
1
)
f(n)=\prod_{i=1}^{k}{(a_i+1)}=(a_1+1)(a_2+1)\cdot\cdot\cdot (a_k+1)
f(n)=i=1∏k(ai+1)=(a1+1)(a2+1)⋅⋅⋅(ak+1)
(3) 约数之和公式
( p 1 0 + p 1 1 + … + p 1 a 1 ) ( p 2 0 + p 2 1 + … + p 2 a 2 ) … ( p k 0 + p k 1 + … p k a k ) (p_1^0+p_1^1+…+p_1^{a_1})(p_2^0+p_2^1+…+p_2^{a_2})…(p_k^0+p_k^1+…p_k^{a_k}) (p10+p11+…+p1a1)(p20+p21+…+p2a2)…(pk0+pk1+…pkak)
O(logn)计算下式:
p
0
+
p
1
+
…
+
p
k
p^0+p^1+…+p^k
p0+p1+…+pk
int sum(int p, int k)
{
if(k == 1) return 1;
if(k % 2 == 0) return (1 + qpow(p, k / 2)) * sum(p, k / 2) % mod;
return (sum(p, k - 1) + qpow(p, k - 1)) % mod;
}
三、欧拉函数
定义:
ϕ
(
n
)
表
示
:
1
到
n
中
与
n
互
质
的
数
的
个
数
\phi(n)表示:1到n中与n互质的数的个数
ϕ(n)表示:1到n中与n互质的数的个数
a与b互质:a与b没有公因子
(1) 欧拉函数公式
先对N
分解质因数得到:
N
=
∏
i
=
1
k
p
i
a
i
=
p
1
a
1
⋅
p
2
a
2
⋅
⋅
⋅
p
k
a
k
N=\prod_{i=1}^{k}{p_i^{a_i}}=p_1^{a_1}\cdot p_2^{a_2}\cdot\cdot\cdot p_k^{a_k}
N=i=1∏kpiai=p1a1⋅p2a2⋅⋅⋅pkak
则欧拉函数公式为:
ϕ
(
N
)
=
N
(
1
−
1
p
1
)
(
1
−
1
p
2
)
⋅
⋅
⋅
(
1
−
1
p
k
)
\phi(N)=N(1-\frac{1}{p_1})(1-\frac{1}{p_2}) \cdot\cdot\,\cdot(1-\frac{1}{p_k})
ϕ(N)=N(1−p11)(1−p21)⋅⋅⋅(1−pk1)
该公式证明:
根据容斥原理得出,所有和N互质的数的个数应该为:
N
−
N
p
1
−
N
p
2
−
⋅
⋅
⋅
−
N
p
k
+
N
p
1
p
2
+
N
p
1
p
3
+
⋅
⋅
⋅
+
N
p
i
p
j
+
⋅
⋅
⋅
−
N
p
1
p
2
p
3
−
N
p
1
p
2
p
4
−
⋅
⋅
⋅
+
N
p
1
p
2
p
3
p
4
−
⋅
⋅
⋅
N-\frac{N}{p_1}-\frac{N}{p_2}-\cdot\cdot\cdot\,-\frac{N}{p_k}+ \\\frac{N}{p_1p_2}+\frac{N}{p_1p_3}+\cdot\cdot\cdot\,+\frac{N}{p_ip_j}+\cdot\cdot\cdot\, \\-\frac{N}{p_1p_2p_3}-\frac{N}{p_1p_2p_4}-\cdot\cdot\cdot+\frac{N}{p_1p_2p_3p_4}-\cdot \cdot \cdot
N−p1N−p2N−⋅⋅⋅−pkN+p1p2N+p1p3N+⋅⋅⋅+pipjN+⋅⋅⋅−p1p2p3N−p1p2p4N−⋅⋅⋅+p1p2p3p4N−⋅⋅⋅
以此类推,上式化简得该公式。
(2) 筛法求欧拉函数
可根据线性筛,在筛出素数的同时筛出欧拉函数。
首先,根据欧拉函数的定义,一个素数p的欧拉函数即1~p中与p互质的数的个数,因此有下式:
ϕ
(
p
)
=
p
−
1
,
p
是
素
数
\phi(p) = p - 1, p是素数
ϕ(p)=p−1,p是素数
在线性筛中,对于一个合数N
来说,从欧拉公式出发,先对N
进行质因数分解,公式计算得:
ϕ
(
N
)
=
N
(
1
−
1
p
1
)
(
1
−
1
p
2
)
⋅
⋅
⋅
(
1
−
1
p
k
)
\phi(N)=N(1-\frac{1}{p_1})(1-\frac{1}{p_2}) \cdot\cdot\,\cdot(1-\frac{1}{p_k})
ϕ(N)=N(1−p11)(1−p21)⋅⋅⋅(1−pk1)
我们可以发现公式中,欧拉函数的值与分解质因数后的指数无关,而N
乘上一个质因子
p
j
p_j
pj后,就要分两种情况讨论:
① p j 是 N 约 数 , 即 N % p j = 0 : p_j是N约数,即N\;\%\;p_j=0\;: pj是N约数,即N%pj=0:
此时
p
j
∈
{
p
1
,
p
2
,
.
.
.
,
p
k
}
p_j \in \{p_1,p_2,...,p_k\}
pj∈{p1,p2,...,pk}
因此有:
ϕ
(
N
×
p
j
)
=
N
⋅
p
j
(
1
−
1
p
1
)
(
1
−
1
p
2
)
⋅
⋅
⋅
(
1
−
1
p
k
)
=
ϕ
(
N
)
⋅
p
j
\phi(N\times p_j)=N\cdot p_j(1-\frac{1}{p_1})(1-\frac{1}{p_2}) \cdot\cdot\,\cdot(1-\frac{1}{p_k})=\phi(N)\,\cdot p_j
ϕ(N×pj)=N⋅pj(1−p11)(1−p21)⋅⋅⋅(1−pk1)=ϕ(N)⋅pj
② p j 不 是 N 约 数 , 即 N % p j ≠ 0 : p_j不是N约数,即N\;\%\;p_j \not ={0}\;: pj不是N约数,即N%pj=0:
此时
p
j
∉
{
p
1
,
p
2
,
.
.
.
,
p
k
}
p_j \notin \{p_1,p_2,...,p_k\}
pj∈/{p1,p2,...,pk}
因此有:
ϕ
(
N
×
p
j
)
=
N
⋅
p
j
(
1
−
1
p
1
)
(
1
−
1
p
2
)
⋅
⋅
⋅
(
1
−
1
p
k
)
⋅
(
1
−
1
p
j
)
=
ϕ
(
N
)
⋅
(
p
j
−
1
)
\phi(N\times p_j)=N\cdot p_j(1-\frac{1}{p_1})(1-\frac{1}{p_2}) \cdot\cdot\,\cdot(1-\frac{1}{p_k})\cdot(1-\frac{1}{p_j})=\phi(N)\,\cdot (p_j - 1)
ϕ(N×pj)=N⋅pj(1−p11)(1−p21)⋅⋅⋅(1−pk1)⋅(1−pj1)=ϕ(N)⋅(pj−1)
因此利用线性筛同时筛出欧拉函数。
int primes[N], cnt;
int phi[N];
bool st[N];
void get_eulers(int n)
{
phi[1] = 1;
for(int i = 2; i <= n; ++i)
{
if(!st[i])
{
primes[cnt++] = i;
phi[i] = i - 1;
}
for(int j = 0; primes[j] <= n / i; ++j)
{
st[primes[j] * i] = 1;
if(i % primes[j] == 0)
{
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
}
四、快速幂
快速幂模板
typedef long long ll;
ll qpow(ll a, ll b, ll p)
{
ll res = 1;
while(b)
{
if(b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
快速幂求逆元(费马小定理)
乘法逆元的定义:
b
和
m
互
质
,
a
整
除
b
,
a
/
b
≡
a
×
x
(
m
o
d
m
)
b和m互质,a整除b,\;a/b\equiv a\times x\;(mod\;m)
b和m互质,a整除b,a/b≡a×x(modm)
我们希望不做除法,因为取余数对于除法是很麻烦的一件事。我们希望把除法变成乘法,如果能找到一个数 x x x,满足 a / b m o d m = a × x m o d m a/b\;mod \;m = a\times x\;mod\;m a/bmodm=a×xmodm,那么我们就把 x x x叫做 b b b的模 m m m的逆元,记作: a / b ≡ a ⋅ b − 1 ( m o d m ) a/b\;\equiv a \cdot b^{-1}\;(mod\;m) a/b≡a⋅b−1(modm)
逆元有什么样的性质?
① b ⋅ b − 1 ≡ 1 ( m o d m ) b\cdot b^{-1}\equiv 1\;(mod\;m) b⋅b−1≡1(modm)
求逆元的问题可以转化为什么?
找到一个 x x x,使得 b ⋅ x ≡ 1 ( m o d m ) b\cdot x\equiv 1\;(mod\;m) b⋅x≡1(modm),即 b ⋅ x m o d m = 1 b\cdot x\;mod\;m=1 b⋅xmodm=1
费马小定理:
p 是 素 数 , b p − 1 ≡ 1 ( m o d p ) p是素数,b^{p-1}\equiv 1\;(mod\;p) p是素数,bp−1≡1(modp)
根据费马小定理,可知
b
⋅
b
p
−
2
≡
1
(
m
o
d
m
)
b\cdot b^{p-2} \equiv 1\;\;(mod\;\;m)
b⋅bp−2≡1(modm)
所以
b
p
−
2
b^{p-2}
bp−2 是
b
b
b 的逆元
下面考虑什么时候 b b b的逆元不存在?显然,当b是p的倍数时,逆元不存在。
接下俩,就可以直接通过快速幂直接求出 b b b的逆元。
int b, p;
cin >> b >> p;
if(b % p) cout << qpow(b, p - 2, p) << endl;
else cout << "impossible" << endl;
逆元可以用来干啥?
观察下列四个式子:
(a + b) % p = (a % p + b % p) % p 成立
(a - b) % p = (a % p - b % p) % p 成立
(a * b) % p = (a % p * b % p) %p 成立
(a / b) % p = (a % p / b % p) % p 不成立
只有在除法的时候我们无法通过直接展开取模最后再取模的方式来得到最后的结果,通过逆元,就可以将上式化为:
(
a
/
b
)
%
p
=
(
a
⋅
b
−
1
)
%
p
(a\;/\;b)\;\%\;p = (a\;\cdot\;b^{-1})\;\%\;p
(a/b)%p=(a⋅b−1)%p
这样就可以通过上述方法来求了。
五、拓展欧几里得算法
问题描述:
给定 n n n 对正整数 a i , b i a_i,b_i ai,bi,对于每对数,求出一组 x i , y i x_i,y_i xi,yi,使其满足 a i × x i + b i × y i = g c d ( a i , b i ) a_i×x_i+b_i×y_i=gcd(a_i,b_i) ai×xi+bi×yi=gcd(ai,bi)。
求 出 一 组 x , y 满 足 : a x + b y = g c d ( a , b ) 求出一组x,y满足:ax+by=gcd(a,b) 求出一组x,y满足:ax+by=gcd(a,b)
裴蜀定理:
对于任意正整数 a , b a,b a,b,一定存在非零整数 x , y x, y x,y,使得 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)
a x + b y = d ax+by=d ax+by=d, d d d 一定是 g c d ( a , b ) gcd(a,b) gcd(a,b) 的倍数,即a和b能凑出来的最小正整数就是a和b的最大公因数
拓展欧几里得算法就是构造一组 x , y x,y x,y,使其满足 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b),我们可以根据欧几里得算法递归的过程中记录下来其系数,推导过程如下:
b
=
0
b=0
b=0 时,
a
a
a 和
b
b
b 的最大公因数就是
a
a
a
因此构造
a
x
+
b
y
=
a
ax+by=a
ax+by=a,即
x
=
1
,
y
=
0
x=1,y=0
x=1,y=0。
b ≠ 0 b\neq 0 b=0时,对于 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b),根据欧几里得算法, g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)
因此要构造 b y + ( a % b ) x = g c d ( a , b ) by+(a\%b)x=gcd(a,b) by+(a%b)x=gcd(a,b)
又 a % b = a − f l o o r ( a b ) a\%b=a-floor(\frac{a}{b}) a%b=a−floor(ba),代入可得, a x + b ( y − f l o o r ( a b ) ⋅ x ) = g c d ( a , b ) ax+b(y-floor(\frac{a}{b})\cdot x) = gcd(a,b) ax+b(y−floor(ba)⋅x)=gcd(a,b)
因此拓展欧几里得算法如下:
int exgcd(int a, int b, int &x, int &y)
{
if(b == 0)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y = y - a / b * x;
return d;
}
(1) 求解线性同余方程
给定 a , b , m a,b,m a,b,m,解线性同余方程: a x ≡ b ( m o d m ) ax\equiv b\;(mod\;m) ax≡b(modm),判断是否有解,有解求出 x x x。
问题等同于求解一个 x x x, a x m o d m = b m o d m ax\;mod\;m=b\;mod\;m axmodm=bmodm
因此, a x ax ax等于 m m m的若干倍加上 b m o d m b\;mod\;m bmodm
a x ≡ b ( m o d m ) ⇔ ax\equiv b\;(mod\;m) \Leftrightarrow ax≡b(modm)⇔
∃ y ∈ Z , s . t . a x = m y + b \exist\; y \in Z,s.t.\;ax=my+b ∃y∈Z,s.t.ax=my+b
⇒ a x − m y = b , b ∈ Z \Rightarrow ax-my=b,b\in Z ⇒ax−my=b,b∈Z
令 y ′ = − y y^{'}=-y y′=−y
⇒ a x + m y ′ = b , b ∈ Z \Rightarrow ax+my^{'}=b,b\in Z ⇒ax+my′=b,b∈Z
即解上面这个方程:
只要 ( a , m ) ∣ b (a,m)\;|\;b (a,m)∣b 即 b b b能整除 a a a和 m m m的最大公约数,那么它就有解,如果b不能整除 a a a和 m m m的最大公约数,它就无解。
解 a x + m y ′ = g c d ( a , m ) , b = k × g c d ( a , m ) , 即 倍 数 为 k , g c d ( a , m ) = d , 则 k = b d 解ax+my^{'}=gcd(a,m),b=k\times gcd(a,m),即倍数为k,gcd(a,m)=d,则k=\frac{b}{d} 解ax+my′=gcd(a,m),b=k×gcd(a,m),即倍数为k,gcd(a,m)=d,则k=db
k × ( a x + m y ′ ) = k × d k\times(ax+my^{'})=k\times d k×(ax+my′)=k×d
因此通过拓展欧几里得算法即求解出一组 x , y x,y x,y, k x 就 是 答 案 kx就是答案 kx就是答案
typedef long long ll;
//ax = b (mod m)
int x, y, a, b;
cin >> a >> m >> b;
int d = exgcd(a, m, x, y);
if(b % d) printf("impossible");
int res = (ll)b / d * x % m;
(2) 拓展欧几里得算法求逆元
求乘法逆元实际上就是解一个线性同余方程:
b
x
≡
1
(
m
o
d
m
)
bx\equiv 1\;(mod\;m)
bx≡1(modm)
即: b x + m y = 1 bx+my=1 bx+my=1
求 b b b的逆元:
int mod_reverse(int b, int m)
{
int d, x, y;
d = exgcd(b, m, x, y);
if(d == 1)
return (x % m + m) % m;
else
return -1;
}