在数论,对正整数 n n n,欧拉函数是小于等于 n n n的正整数中与 n n n互质的数的数目.
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个互不相同的质数。
证明:
可以由容斥原理证明。
ϕ ( 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
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;
}
设 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
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;
}
对于求 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)!
做法:
相关题目:AcWing 875. 快速幂
#include
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)
乘法逆元的定义:
若整数 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
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;
}
裴蜀定理: 若 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
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;
}
求出一个 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
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;
}
中国剩余定理: 假设整数 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
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;
}