文章目录
一、欧拉函数
在数论,对正整数 n n n,欧拉函数是小于等于 n n n的正整数中与 n n n互质的数的数目.
1.欧拉函数
1
∼
N
1 \sim N
1∼N 中与
N
N
N 互质的数的个数被称为欧拉函数,记为
ϕ
(
N
)
\phi(N)
ϕ(N) 。
若在算数基本定理中,
N
=
p
1
a
1
p
2
a
2
…
p
m
a
m
N=p_{1}^{a_{1}} p_{2}^{a_{2}} \ldots p_{m}^{a_{m}}
N=p1a1p2a2…pmam ,则:
ϕ
(
N
)
=
N
×
p
1
−
1
p
1
×
p
2
−
1
p
2
×
…
×
p
m
−
1
p
m
\phi(N)=N \times \frac{p_{1}-1}{p_{1}} \times \frac{p_{2}-1}{p_{2}} \times \ldots \times \frac{p_{m}-1}{p_{m}}
ϕ(N)=N×p1p1−1×p2p2−1×…×pmpm−1
其中
p
1
p_1
p1~
p
m
p_m
pm为
m
m
m个互不相同的质数。
证明:
可以由容斥原理证明。
- 删去 p 1 , p 2 , . . . , p m p_1,p_2,...,p_m p1,p2,...,pm的倍数
- 加上 p 1 p 2 , p 1 p 3 , . . . p_1p_2,p_1p_3,... p1p2,p1p3,...的倍数
- …
ϕ
(
n
)
=
N
−
(
N
p
1
+
N
p
2
+
.
.
.
N
p
m
)
+
(
N
p
1
p
2
+
N
p
1
p
3
+
.
.
.
)
−
(
N
p
1
p
2
p
3
+
N
p
1
p
2
p
4
+
.
.
.
)
+
.
.
.
\phi(n)=N-(\frac{N}{p_1}+\frac{N}{p_2}+...\frac{N}{p_m})+(\frac{N}{p_1p_2}+\frac{N}{p_1p_3}+...)-(\frac{N}{p_1p_2p_3}+\frac{N}{p_1p_2p_4}+...)+...
ϕ(n)=N−(p1N+p2N+...pmN)+(p1p2N+p1p3N+...)−(p1p2p3N+p1p2p4N+...)+...
整理一下可得:
ϕ
(
N
)
=
N
×
p
1
−
1
p
1
×
p
2
−
1
p
2
×
…
×
p
m
−
1
p
m
\phi(N)=N \times \frac{p_{1}-1}{p_{1}} \times \frac{p_{2}-1}{p_{2}} \times \ldots \times \frac{p_{m}-1}{p_{m}}
ϕ(N)=N×p1p1−1×p2p2−1×…×pmpm−1
ex:
ϕ
(
6
)
=
6
∗
1
2
∗
2
3
=
2
\phi(6)=6*\frac{1}{2}*\frac{2}{3}=2
ϕ(6)=6∗21∗32=2
相关题目:AcWing 873. 欧拉函数
#include <iostream>
using namespace std;
int main()
{
int n;
scanf("%d", &n);
while(n --)
{
int x;
scanf("%d", &x);
int phi = x;
for(int i = 2; i <= x / i; i ++ )
{
if(x % i == 0)
{
phi = phi / i * (i - 1);
while(x % i == 0) x /= i;
}
}
if(x > 1) phi = phi / x * (x - 1);
printf("%d\n", phi);
}
return 0;
}
2.筛法求欧拉函数(采用筛质数的线性筛法)
设 p j p_j pj是1~ i i i中的一个质数,对于一个欧拉函数 ϕ ( i ) \phi(i) ϕ(i)有如下性质:
-
若 i i i为一个质数,则1~ i i i中一定有 ϕ ( i ) = i − 1 \phi(i) = i -1 ϕ(i)=i−1
-
若 p j p_j pj不是 i i i的最小质因子,可得 i i i的最小质因子一定比 p j p_j pj大,所以有: ϕ ( i ) = i × p 1 − 1 p 1 × p 2 − 1 p 2 × … × p m − 1 p m (1) \phi(i)=i \times \frac{p_{1}-1}{p_{1}} \times \frac{p_{2}-1}{p_{2}} \times \ldots \times \frac{p_{m}-1}{p_{m}}\tag1 ϕ(i)=i×p1p1−1×p2p2−1×…×pmpm−1(1)
则 p j p_j pj一定是 i × p j i\times p_{j} i×pj的最小质因子,有: ϕ ( i × p j ) = i × p j × p j − 1 p j × p 1 − 1 p 1 × p 2 − 1 p 2 × … × p m − 1 p m (2) \phi(i\times p_j)=i \times p_j \times \frac{p_j-1} {p_j} \times \frac{p_{1}-1}{p_{1}} \times \frac{p_{2}-1}{p_{2}} \times \ldots \times \frac{p_{m}-1}{p_{m}}\tag2 ϕ(i×pj)=i×pj×pjpj−1×p1p1−1×p2p2−1×…×pmpm−1(2)
将(1)带入(2)中有:
ϕ ( i × p j ) = ϕ ( i ) × ( p j − 1 ) \phi(i \times p_j)=\phi(i) \times (p_j-1) ϕ(i×pj)=ϕ(i)×(pj−1) -
若 p j p_j pj是 i i i的最小质因子,所以有: ϕ ( i ) = i × p 1 − 1 p 1 × p 2 − 1 p 2 × … × p j − 1 p j × … × p m − 1 p m (1) \phi(i)=i \times \frac{p_{1}-1}{p_{1}} \times \frac{p_{2}-1}{p_{2}} \times \ldots \times\frac{p_j-1} {p_j} \times \ldots \times \frac{p_{m}-1}{p_{m}}\tag1 ϕ(i)=i×p1p1−1×p2p2−1×…×pjpj−1×…×pmpm−1(1)
则 p j p_j pj一定是 i × p j i\times p_{j} i×pj的最小质因子,有: ϕ ( i × p j ) = i × p j × p 1 − 1 p 1 × p 2 − 1 p 2 × … × p j − 1 p j × … × p m − 1 p m (2) \phi(i\times p_j)=i \times p_j \times \frac{p_{1}-1}{p_{1}} \times \frac{p_{2}-1}{p_{2}} \times \ldots \times\frac{p_j-1} {p_j} \times \ldots \times \frac{p_{m}-1}{p_{m}}\tag2 ϕ(i×pj)=i×pj×p1p1−1×p2p2−1×…×pjpj−1×…×pmpm−1(2)
将(1)带入(2)中有:
ϕ ( i × p j ) = p j × ϕ ( i ) \phi(i \times p_j)=p_j \times \phi(i) ϕ(i×pj)=pj×ϕ(i)
相关题目:Acwing 874. 筛法求欧拉函数
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
int primes[N], cnt;
int phi[N];
bool st[N];
int main()
{
int n;
scanf("%d", &n);
phi[1] = 1;
for(int i = 2; i <= n; i ++ )
{
if(!st[i])
{
phi[i] = i - 1;
primes[cnt ++ ] = i;
}
for(int j = 0; primes[j] <= n / i; j ++ )
{
int t = i * primes[j];
st[t] = true;
if(i % primes[j] == 0)
{
phi[t] = phi[i] * primes[j];
break;
}
phi[t] = phi[i] * (primes[j] - 1);
}
}
LL res = 0;
for(int i = 1; i <= n; i ++ ) res += phi[i];
printf("%lld\n", res);
return 0;
}
二、快速幂
1.快速幂
对于求
a
k
a^k
ak,朴素做法是将
a
a
a累乘
k
k
k次,其时间复杂度为
O
(
n
)
O(n)
O(n)!有一种特别求幂的方法可以将时间复杂度降低到
O
(
l
o
g
n
)
O(logn)
O(logn)!
做法:
- 先预处理 a 2 0 , a 2 1 , a 2 2 , . . . , a 2 l o g k a^{2^{0}},a^{2^{1}},a^{2^{2}},...,a^{2^{logk}} a20,a21,a22,...,a2logk,花费的时间为: l o g k logk logk
- 其中 a 2 1 = ( a 2 0 ) 2 , a 2 2 = ( a 2 1 ) 2 , . . . . . . , a 2 l o g k = ( a 2 l o g k − 1 ) 2 a^{2^{1}}=(a^{2^{0}})^2,a^{2^{2}}=(a^{2^{1}})^2,......,a^{2^{logk}}=(a^{2^{logk}-1})^2 a21=(a20)2,a22=(a21)2,......,a2logk=(a2logk−1)2,结论每一个 a a a都可以写成上一个 a a a的平方!
- 可以将 a k a^k ak拆成 a 2 x 1 × a 2 x 2 × a 2 x 3 × . . . . . . × a 2 x t a^{2^{x_1}} \times a^{2^{x_2}} \times a^{2^{x_3}} \times ...... \times a^{2^{x_t}} a2x1×a2x2×a2x3×......×a2xt
- a k = a 2 x 1 + 2 x 2 + . . . . . . + 2 x t a^k =a^{2^{x_1} + 2^{x_2}+......+2^{x_t}} ak=a2x1+2x2+......+2xt,即 k = 2 x 1 + 2 x 2 + . . . . . . + 2 x t k = 2^{x_1} + 2^{x_2}+......+2^{x_t} k=2x1+2x2+......+2xt,为 k k k的二进制数的表示!
相关题目:AcWing 875. 快速幂
#include <iostream>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p)
{
LL res = 1 % p;
while(b)
{
if(b & 1) res = res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main()
{
int n;
scanf("%d", &n);
while(n -- )
{
int a, b, p;
scanf("%d%d%d", &a, &b, &p);
printf("%lld\n", qmi(a, b, p));
}
return 0;
}
时间复杂度: O ( l o g n ) O(logn) O(logn)
2.快速幂求逆元
乘法逆元的定义:
若整数
b
,
m
b, m
b,m 互质,并且对于任意的整数
a
a
a ,如果满足
b
∣
a
b \mid a
b∣a ,则存在一个整数
x
x
x ,使得
a
/
b
≡
a
×
x
(
m
o
d
m
)
a / b \equiv a \times x(\bmod m)
a/b≡a×x(modm) ,则称
x
x
x 为
b
b
b 的模
m
m
m 乘法逆元,记为
b
−
1
(
m
o
d
m
)
b^{-1}(\bmod m)
b−1(modm) 。
b
b
b 存在乘法逆元的充要条件是
b
b
b 与模数
m
m
m 互质。当模数
m
m
m 为质数时,
b
m
−
2
b^{m-2}
bm−2 即为
b
b
b 的乘法逆元。
欧拉定理:
设
a
,
m
∈
N
+
a, m \in N^{+}
a,m∈N+,且
gcd
(
a
,
m
)
=
1
\operatorname{gcd}(a, m)=1
gcd(a,m)=1 ,则我们有:
a
φ
(
m
)
≡
1
(
m
o
d
m
)
a^{\varphi(m)} \equiv 1(\bmod m)
aφ(m)≡1(modm)
小费马定理:
如果
p
p
p是一个质数,而整数
a
a
a不是
p
p
p的倍数,则有
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1}≡1(mod\ p)
ap−1≡1(mod p)。
证明:可以由欧拉定理求得,
p
p
p是一个质数,则有
ϕ
(
p
)
=
p
−
1
\phi(p)=p-1
ϕ(p)=p−1,即可得到
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1} \equiv 1(mod \ p)
ap−1≡1(mod p)
相关题目链接:AcWing 876. 快速幂求逆元
#include <iostream>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p)
{
LL res = 1 % p;
while(b)
{
if(b & 1) res = res * a % p;
a = (LL) a * a % p;
b >>= 1;
}
return res;
}
int main()
{
int n;
scanf("%d", &n);
while(n -- )
{
int a, p;
scanf("%d%d", &a, &p);
LL t = qmi(a, p - 2, p);
if(t * a % p == 1) printf("%lld\n", t);
else puts("impossible");
}
return 0;
}
三、扩展欧几里得算法
1.扩展欧几里得算法
裴蜀定理: 若 a , b a,b a,b是整数,且 g c d ( a , b ) = d gcd(a,b)=d gcd(a,b)=d,那么对于任意的整数 x , y x,y x,y, a x + b y ax+by ax+by都一定是 d d d的倍数,特别地,一定存在整数 x , y x,y x,y,使 a x + b y = d ax+by=d ax+by=d成立。
a
x
+
b
y
=
d
ax+by=d
ax+by=d,对于下一状态:
e
x
g
c
d
(
b
,
a
%
b
,
y
,
x
)
exgcd(b,a \%b,y,x)
exgcd(b,a%b,y,x),有方程:
b
y
+
(
a
−
⌊
a
b
⌋
×
b
)
x
=
d
by+(a- \lfloor \frac{a}{b} \rfloor \times b)x=d
by+(a−⌊ba⌋×b)x=d,整理一下:
a
x
+
b
(
y
−
⌊
a
b
⌋
×
x
)
=
d
ax+b(y-\lfloor \frac{a}{b} \rfloor \times x)=d
ax+b(y−⌊ba⌋×x)=d
AcWing 877. 扩展欧几里得算法
#include <iostream>
using namespace std;
int exgcd(int a, int b, int &x, int &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
int n;
scanf("%d", &n);
while(n -- )
{
int a, b, x, y;
scanf("%d%d", &a, &b);
exgcd(a, b, x, y);
printf("%d %d\n", x, y);
}
return 0;
}
2.线性同余方程
求出一个
x
i
x_i
xi使得
a
i
×
x
i
≡
b
i
(
m
o
d
m
i
)
a_{i} \times x_{i} \equiv b_{i}\left(\bmod m_{i}\right)
ai×xi≡bi(modmi)成立。
对于
a
×
x
≡
b
(
m
o
d
m
)
a \times x\equiv b\left(\bmod m\right)
a×x≡b(modm),一定有:
a
x
=
d
m
+
b
⇒
a
x
−
d
m
=
b
ax = dm+b \Rightarrow ax-dm=b
ax=dm+b⇒ax−dm=b,令
−
d
=
y
-d=y
−d=y
则有
a
x
+
m
y
=
b
ax+my=b
ax+my=b,最后做法如上所述!
AcWing 878. 线性同余方程
#include <iostream>
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
int n;
scanf("%d", &n);
while(n -- )
{
LL a, b, m;
scanf("%lld%lld%lld", &a, &b, &m);
LL x, y;
LL d = exgcd(a, m, x, y);
if(b % d) puts("impossible");
else printf("%lld\n", x * b / d % m);
}
return 0;
}
四、中国剩余定理
1.表达整数的奇怪方式
中国剩余定理: 假设整数
m
1
,
m
2
,
.
.
.
,
m
n
m_1,m_2, ... ,m_n
m1,m2,...,mn两两互质,则对任意的整数:
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2, ... ,a_n
a1,a2,...,an,方程组有解,并且通解可以用如下方式构造得到:
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
⋮
x
≡
a
n
(
m
o
d
m
n
)
\left\{\begin{array}{c} x \equiv a_{1}\left(\bmod m_{1}\right) \\ x \equiv a_{2}\left(\bmod m_{2}\right) \\ \vdots \\ x \equiv a_{n}\left(\bmod m_{n}\right) \end{array}\right.
⎩⎪⎪⎪⎨⎪⎪⎪⎧x≡a1(modm1)x≡a2(modm2)⋮x≡an(modmn)
我们可以两两进行构造,
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
⇒
{
x
=
k
1
m
1
+
a
1
x
=
k
2
m
2
+
a
2
\left\{\begin{array}{c}x \equiv a_{1}\left(\bmod m_{1}\right) \\ x \equiv a_{2}\left(\bmod m_{2}\right)\end{array}\right. \Rightarrow \left\{\begin{array}{c}x =k_1 m_{1}+a_1 \\ x =k_{2}m_2+a_2\end{array}\right.
{x≡a1(modm1)x≡a2(modm2)⇒{x=k1m1+a1x=k2m2+a2
有
k
1
m
1
+
a
1
=
k
2
m
2
+
a
2
⇒
k
1
m
1
−
k
2
m
2
=
a
2
−
a
1
k_1m_1+a_1=k_2m_2+a_2 \Rightarrow k_1m_1-k_2m_2=a_2-a_1
k1m1+a1=k2m2+a2⇒k1m1−k2m2=a2−a1
该方程有解的条件是
(
m
1
,
m
2
)
∣
a
2
−
a
1
(m_1,m_2)|a_2-a_1
(m1,m2)∣a2−a1
二元一次不定方程的通解形式:
若二元一次不定方程
a
x
+
b
y
=
n
a x+b y=n
ax+by=n 有解,
x
0
,
y
0
x_{0}, y_{0}
x0,y0 为它的一组整数解,则通解为
{
x
=
x
0
+
b
(
a
,
b
)
⋅
t
y
=
y
0
−
a
(
a
,
b
)
⋅
t
t
∈
Z
\left\{\begin{array}{l} x=x_{0}+\frac{b}{(a, b)} \cdot t \\ y=y_{0}-\frac{a}{(a, b)} \cdot t \end{array} t \in Z\right.
{x=x0+(a,b)b⋅ty=y0−(a,b)a⋅tt∈Z
所以
k
1
m
1
−
k
2
m
2
=
a
2
−
a
1
k_1m_1-k_2m_2=a_2-a_1
k1m1−k2m2=a2−a1的通解有
{
k
1
+
m
2
d
⋅
K
k
2
+
m
1
d
⋅
K
K
∈
Z
\left\{\begin{array}{l} k_{1}+\frac{m_2}{d} \cdot K \\ k_{2}+\frac{m_1}{d} \cdot K \end{array} K \in Z \right.
{k1+dm2⋅Kk2+dm1⋅KK∈Z
将其最小的一个通解带入
x
=
k
1
m
1
+
a
1
x=k_1m_1+a_1
x=k1m1+a1,有
x
=
k
1
m
1
+
m
2
d
m
1
K
+
a
1
x=k_1m_1+\frac{m_2}{d}m_1K+a_1
x=k1m1+dm2m1K+a1
整理一下有:
x
=
k
1
m
1
+
a
1
+
K
m
1
m
2
d
x=k_1m_1+a_1+K\frac{m_1m_2}{d}
x=k1m1+a1+Kdm1m2,
x
=
x
0
+
k
a
x=x_0+ka
x=x0+ka
AcWing 204. 表达整数的奇怪方式
#include <iostream>
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
int n;
scanf("%d", &n);
LL m1, a1;
bool flag = true;
scanf("%lld%lld", &a1, &m1);
for(int i = 0; i < n - 1; i ++ )
{
LL m2, a2;
scanf("%lld%lld", &a2, &m2);
LL k1, k2;
LL d = exgcd(a1, a2, k1, k2);
if((m2 - m1) % d)
{
flag = false;
break;
}
LL t = a2 / d;
k1 *= (m2 - m1) / d;
k1 = (k1 % t + t) % t;
m1 += a1 * k1;
a1 = abs(a1 / d * a2);
}
if(flag) printf("%lld\n", (m1 % a1 + a1) % a1);
else puts("-1");
return 0;
}