当前位置: 首页 > news >正文

【数论】欧拉函数

文章目录

  • 上文链接
  • 一、欧拉函数
    • 1. 欧拉函数的定义
    • 2. 欧拉函数的性质
    • 3. 欧拉函数的求法
    • 4. 代码求解单个数的欧拉函数
    • 5. 欧拉筛打表欧拉函数
  • 二、OJ 练习
    • 1. 仪仗队 ⭐⭐⭐
      • (1) 解题思路
      • (2) 代码实现
    • 2. GCD ⭐⭐⭐⭐
      • (1) 解题思路
      • (2) 代码实现

上文链接

  • 质数筛(埃氏筛、欧拉筛)

一、欧拉函数

1. 欧拉函数的定义

欧拉函数:对于一个正整数 m m m,若小于 m m m 的正整数中与 n n n 互素的数(包括 1 1 1)的个数为 s s s,并定义 φ ( m ) = s \varphi(m)=s φ(m)=s φ ( m ) \varphi(m) φ(m) 称为欧拉函数。


2. 欧拉函数的性质

  • 性质 1:若 a a a b b b 互素,则

φ ( a b ) = φ ( a ) φ ( b ) (1) φ(ab) = φ(a) φ(b)\tag 1 φ(ab)=φ(a)φ(b)(1)

  • 性质 2:若 m , n m,n m,n 满足 m ∣ n m\mid n mn,则

φ ( m ) ∣ φ ( n ) (2) \varphi(m)\mid\varphi(n)\tag 2 φ(m)φ(n)(2)

  • 性质 3: 如果 p p p 是一个素数,则

φ ( p ) = p − 1 (3) φ(p) = p - 1\tag 3 φ(p)=p1(3)

  • 性质 4: 如果 p p p 是素数, k k k 是正整数,则

φ ( p k ) = p k − p k − 1 = p k − 1 ( p − 1 ) (4) φ(p^k) = p^k - p^{k-1} = p^{k-1}(p - 1)\tag4 φ(pk)=pkpk1=pk1(p1)(4)

证明:

φ ( n ) = φ ( p k ) φ(n)=φ(p^k) φ(n)=φ(pk),即 n = p k n=p^k n=pk

那么由于 p p p 是一个素数,所以只要一个数的素因数里不包含 p p p,那么这个数就能与 n n n 互素。反之只要含 p p p 这个素因数,那么就不和 n n n 互素。也就是说像 p , 2 p , ⋯ p k − 1 p p,2p,\cdots p^{k-1}p p,2p,pk1p 这些数都不与 n = p k n=p^k n=pk 互素。那么与 n n n 互素的就有 p k − p k − 1 p^k-p^{k-1} pkpk1 个了,即
φ ( n ) = φ ( p k ) = p k − p k − 1 = p k − 1 ( p − 1 ) φ(n)=φ(p^k)= p^k - p^{k-1} = p^{k-1}(p - 1) φ(n)=φ(pk)=pkpk1=pk1(p1)

  • ==性质 5:==由算数基本定理,对于 n n n 的质因数分解为 n = p 1 k 1 p 2 k 2 ⋯ p m k m n = p_1^{k_1} p_2^{k_2} \cdots p_m^{k_m} n=p1k1p2k2pmkm,则
    φ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋯ ( 1 − 1 p m ) (5) φ(n) = n \left(1 - \frac{1}{p_1}\right) \left(1 - \frac{1}{p_2}\right) \cdots \left(1 - \frac{1}{p_m}\right)\tag5 φ(n)=n(1p11)(1p21)(1pm1)(5)

证明:利用性质 1 φ ( n ) φ(n) φ(n) 可以表示为各素因数的积,即
φ ( n ) = φ ( p 1 k 1 ) ⋅ φ ( p 2 k 2 ) ⋯ φ ( p m k m ) φ(n) = φ(p_1^{k_1}) \cdot φ(p_2^{k_2}) \cdots φ(p_m^{k_m}) φ(n)=φ(p1k1)φ(p2k2)φ(pmkm)
能这样拆分是因为 p 1 , p 2 , ⋯ p m p_1,p_2,\cdots p_m p1,p2,pm 都是不同的素数,因此无论它们乘了多少次方,又或者把它们乘方之后拿其中几个相乘,它们之间一定不会有相同的因数的。换句话说, p 1 k 1 , p 2 k 2 , ⋯ p m k m p_1^{k_1},p_2^{k_2},\cdots p_m^{k_m} p1k1,p2k2,pmkm 这些数两两之间都是互素的。因此满足性质 1 的条件——“若 a a a b b b 互素”。

于是,我们根据性质 4 进一步展开为:
φ ( n ) = p 1 k 1 ( 1 − 1 p 1 ) ⋅ p 2 k 2 ( 1 − 1 p 2 ) ⋯ p m k m ( 1 − 1 p m ) \varphi (n)=p_1^{k_1} \left(1 - \frac{1}{p_1}\right) \cdot p_2^{k_2} \left(1 - \frac{1}{p_2}\right) \cdots p_m^{k_m} \left(1 - \frac{1}{p_m}\right) φ(n)=p1k1(1p11)p2k2(1p21)pmkm(1pm1)

把括号外的所有 p i k i p_i^{k_i} piki 合并起来,会发现 p 1 k 1 p 2 k 2 ⋯ p m k m p_1^{k_1} p_2^{k_2} \cdots p_m^{k_m} p1k1p2k2pmkm 就是 n n n。于是
φ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋯ ( 1 − 1 p m ) φ(n) = n \left(1 - \frac{1}{p_1}\right) \left(1 - \frac{1}{p_2}\right) \cdots \left(1 - \frac{1}{p_m}\right) φ(n)=n(1p11)(1p21)(1pm1)
证毕!


3. 欧拉函数的求法

  • 根据定义进行求解

例如,我们求解 φ ( 10 ) \varphi(10) φ(10),可以把所有小于10的正整数全部列出来: { 1 , 2 , ⋯ 9 , 10 } \{1,2,\cdots9,10\} {1,2,9,10},再去掉与10不互素的数,最后 φ ( 10 ) = 4 \varphi(10)=4 φ(10)=4
当然这个方法确实挺万能的,前提是你有时间…

  • 根据性质 3 进行求解

m m m 是一个素数,可以直接根据上面提到的(3)式进行求解
例如, φ ( 7 ) = 7 − 1 = 6 \varphi(7)=7-1=6 φ(7)=71=6

  • 性质 1 + 性质 3

m m m 不是一个素数,我们可以根据性质 1 将其拆分为两个欧拉函数的乘积的形式,再通过性质 3,也就是(3)式进行求解
例如, φ ( 66 ) = φ ( 6 ) φ ( 11 ) = φ ( 2 ) φ ( 3 ) φ ( 11 ) = ( 2 − 1 ) × ( 3 − 1 ) × ( 11 − 1 ) = 20 \varphi(66)=\varphi(6)\varphi(11)=\varphi(2)\varphi(3)\varphi(11)=(2-1)\times(3-1)\times(11-1)=20 φ(66)=φ(6)φ(11)=φ(2)φ(3)φ(11)=(21)×(31)×(111)=20

(注意拆出的两个数一定要是互素的,否则不能这样拆)

  • 用通式进行求解

当然,欧拉函数的求法有一个通式,也就是我们上面提到的(5)式:
φ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋯ ( 1 − 1 p m ) φ(n) = n \left(1 - \frac{1}{p_1}\right) \left(1 - \frac{1}{p_2}\right) \cdots \left(1 - \frac{1}{p_m}\right) φ(n)=n(1p11)(1p21)(1pm1)


4. 代码求解单个数的欧拉函数

只求解一个数的欧拉函数,我们可以通过用通式进行求解。

int phi(int n)
{int ret = n;for(int i = 2; i <= n / i; i++){// 如果 i 是 n 的质因数if(n % i == 0){ret = ret / i * (i - 1);  // 先除后乘,保证不会溢出 while(n % i == 0) n /= i;}}// 别忘记判断最后⼀个数if(n > 1) ret = ret / n * (n - 1);return ret;
}

时间复杂度为: O ( n ) O(\sqrt{n}) O(n )


5. 欧拉筛打表欧拉函数

如果现在求解 [ 1 , n ] [1, n] [1,n] 中所有数的欧拉函数,那么每个数都用通式求解,时间复杂度会比较高。我们可以对筛质数时用到的欧拉筛进行改造,来实现求解 [ 1 , n ] [1, n] [1,n] 中所有数的欧拉函数。这样我们的时间复杂度就能降至 O ( n ) O(n) O(n)

在线性筛的过程中:

  • 如果该数是质数,那么 φ ( i ) = i − 1 \varphi(i) = i - 1 φ(i)=i1

  • 如果该数是合数,每个合数 x x x 都是被它的最小质因数筛掉的。

    p j p_j pj x x x 的最小质因数,则 x x x 是通过 p j × i p_j\times i pj×i 筛掉的,也就是 x = p j × i x = p_j \times i x=pj×i

    • 如果 p j p_j pj i i i 的因数,那么 i i i 这个数就包含了 x x x 的所有质因子,由欧拉函数的计算公式得:
      φ ( x ) = x ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋯ ( 1 − 1 p m ) = p j × i × ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋯ ( 1 − 1 p m ) = p j × φ ( i ) \begin{aligned} φ(x) &= x \left(1 - \frac{1}{p_1}\right) \left(1 - \frac{1}{p_2}\right) \cdots \left(1 - \frac{1}{p_m}\right) \\ &= p_j \times i\times \left(1 - \frac{1}{p_1}\right) \left(1 - \frac{1}{p_2}\right) \cdots \left(1 - \frac{1}{p_m}\right) \\ &= p_j \times \varphi(i) \end{aligned} φ(x)=x(1p11)(1p21)(1pm1)=pj×i×(1p11)(1p21)(1pm1)=pj×φ(i)

    • 如果 p j p_j pj 不是 i i i 的因数,那么 i i i p j p_j pj 必定互质,则
      φ ( x ) = φ ( p j × i ) = φ ( p j ) × φ ( i ) = ( p j − 1 ) × φ ( i ) \begin{aligned} \varphi(x) &= \varphi(p_j\times i) \\ &= \varphi(p_j) \times \varphi(i) \\ &= (p_j - 1) \times \varphi(i) \end{aligned} φ(x)=φ(pj×i)=φ(pj)×φ(i)=(pj1)×φ(i)

因此,我们可以从前往后用欧拉筛把所有数的欧拉函数求出来。

代码实现:

int p[N], phi[N];  // 分别记录 1-n 中的质数和 1-n 中所有数的欧拉函数
bool vis[N];  // 检查某个数是否被标记为合数
int cnt;  // 记录质数的个数// 线性筛 + 打表欧拉函数
void get_phi(int n)
{// 1 这个数单独处理phi[1] = 1;// 从 2 开始枚举每一个数for(int i = 2; i <= n; i++){// 如果这个数 i 没有被标记,也就是说 i 是质数if(!vis[i]){p[cnt++] = i;  // 记录这个质数phi[i] = i - 1;  // 质数的欧拉函数就是 i - 1}// 标记合数 + 计算欧拉函数for(int j = 0; i * p[j] <= n; j++){int x = i * p[j];vis[x] = true;if(i % p[j] == 0)  // 如果 p[j] 是 i 的因数{phi[x] = phi[i] * p[j];  // 由上面的推导可得break;}else  // i 和 p[j] 互质{phi[x] = phi[i] * (p[j] - 1);  // 由上面的推导可得}}}
}

二、OJ 练习

1. 仪仗队 ⭐⭐⭐

【题目链接】

[P2158 SDOI2008] 仪仗队 - 洛谷

【题目描述】

作为体育委员,C 君负责这次运动会仪仗队的训练。仪仗队是由学生组成的 N × N N \times N N×N 的方阵,为了保证队伍在行进中整齐划一,C 君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。

现在,C 君希望你告诉他队伍整齐时能看到的学生人数。

【输入格式】

一行,一个正整数 N N N

【输出格式】

输出一行一个数,即 C 君应看到的学生人数。

【示例一】

输入

4

输出

9

【说明/提示】

对于 100 % 100 \% 100% 的数据, 1 ≤ N ≤ 40000 1 \le N \le 40000 1N40000


(1) 解题思路

这个问题可以稍加简化一下,由于左上和右下部分能看得见的位置数量一定是完全对称的,所以我们只需考虑这张图的一半即可,像下面标为 1 的位置。

0 0 0 0
0 0 0 1
0 0 1 1
0 1 1 1

以左下角的人为原点建立一个直角坐标系,不难发现,一个位置 ( x , y ) (x, y) (x,y) 如果想要不被挡住,那么它的坐标一定要满足 g c d ( x , y ) = 1 gcd(x, y) = 1 gcd(x,y)=1,也就是横坐标和纵坐标互质。因为如果它们不互质,那么它就一定会被 ( x / g c d ( x , y ) , y / g c d ( x , y ) ) (x/gcd(x, y),\ y/gcd(x, y)) (x/gcd(x,y), y/gcd(x,y)) 挡住。现在我们只需要求解在右下半区域横纵坐标互质的位置个数即可。

在这个区域中, x > y x > y x>y,如果 x x x 固定,想要求解一个 y y y x x x 互质,那么只能从 [ 1 , x − 1 ] [1, x - 1] [1,x1] 中寻找,现在有点感觉了吗?这不就是在求 x x x 的欧拉函数吗!对于每一个 x x x 与它互质的 y y y 的个数就是 x x x 的欧拉函数 φ ( x ) \varphi(x) φ(x)。所以我们只需要对于每一个 x x x 都求一下它的欧拉函数 φ ( x ) \varphi(x) φ(x),然后把它们加起来得到 n n n,那么右下区域能被看到的位置的个数就是 n n n。那么左上半区域能被看到的位置个数也为 n n n,最后再加上对角线上能被看到的一个位置,最终结果就是 2 n + 1 2n + 1 2n+1


(2) 代码实现

#include<iostream>using namespace std;const int N = 4e4 + 10;
int p[N], phi[N];
bool vis[N];
int cnt;// 把 1-n 中所有的欧拉函数都求出来放到 phi 数组
void get_phi(int n)
{phi[1] = 1;  // 1 单独处理for(int i = 2; i <= n; i++){// i 是质数if(!vis[i]){p[cnt++] = i;phi[i] = i - 1;  // 质数的欧拉函数是 i - 1}for(int j = 0; i * p[j] <= n; j++){int x = i * p[j];vis[x] = true;if(i % p[j] == 0){phi[x] = phi[i] * p[j];break;}else  // i 和 p[j] 互质{phi[x] = phi[i] * (p[j] - 1);}}}
}int main()
{int n;cin >> n;// 边长为 n, 但坐标从 0 开始,因此只需求到 n-1get_phi(n - 1);int sum = 0;for(int i = 1; i < n; i++) sum += phi[i];if(n == 1) cout << 0 << endl;else cout << (sum * 2 + 1) << endl;return 0;
}

2. GCD ⭐⭐⭐⭐

【题目链接】

P2568 GCD - 洛谷

【题目描述】

给定正整数 n n n,求 1 ≤ x , y ≤ n 1\le x,y\le n 1x,yn gcd ⁡ ( x , y ) \gcd(x,y) gcd(x,y) 为素数的数对 ( x , y ) (x,y) (x,y) 有多少对。

【输入格式】

只有一行一个整数,代表 n n n

【输出格式】

一行一个整数表示答案。

【示例一】

输入

4

输出

4

【说明/提示】

样例输入输出 1 解释

对于样例,满足条件的 ( x , y ) (x,y) (x,y) ( 2 , 2 ) (2,2) (2,2) ( 2 , 4 ) (2,4) (2,4) ( 3 , 3 ) (3,3) (3,3) ( 4 , 2 ) (4,2) (4,2)


数据规模与约定

  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 1 0 7 1\le n\le10^7 1n107

(1) 解题思路

假设 g c d ( x , y ) = d gcd(x, y) = d gcd(x,y)=d,则 x x x y y y 一定是 d d d 的倍数,那么我们要找 x x x y y y 必定是从 { d , 2 d , 3 d , ⋯ , ⌊ n d ⌋ d d, 2d, 3d, \cdots, \lfloor\frac{n}{d}\rfloor d d,2d,3d,,dnd } 这些数中寻找。现在假设 x = a d , y = b d x = ad,\ y = bd x=ad, y=bd,则 g c d ( x , y ) = d × g c d ( a , b ) gcd(x, y) = d \times gcd(a, b) gcd(x,y)=d×gcd(a,b),由于题目要求 g c d ( x , y ) gcd(x, y) gcd(x,y) 必须是质数,也就是说 d d d 要为质数,所以 g c d ( a , b ) gcd(a, b) gcd(a,b) 必须等于 1 1 1,即 a , b a, b a,b 必须互质!

现在我们从小到大枚举质数作为 d d d,对于每一个 d d d,去构造 x , y x, y x,y。假设我构造出了 x = 5 d x = 5d x=5d,那么 y y y 等于多少倍的 d d d 呢?根据上面的认识,这里 d d d 前面的系数必须要与 5 5 5 互质才行,所以 y y y 的取值总共就有 φ ( 5 ) \varphi(5) φ(5) 个。

所以现在我们知道了,首先从小到达枚举质数 d d d,之后用 1 d , 2 d , 3 d , ⋯ 1d, 2d, 3d,\cdots 1d,2d,3d, 一直到 ⌊ n d ⌋ d \lfloor\frac{n}{d}\rfloor d dnd 作为 x x x,它们对应的 y y y 的取值个数分别是 φ ( 1 ) , φ ( 2 ) , φ ( 3 ) , ⋯ φ ( ⌊ n d ⌋ ) \varphi(1), \varphi(2), \varphi(3),\cdots \varphi(\lfloor\frac{n}{d}\rfloor) φ(1),φ(2),φ(3),φ(⌊dn⌋),总共 ∑ i = 1 ⌊ n d ⌋ φ ( i ) \sum\limits_{i = 1}^{\lfloor\frac{n}{d}\rfloor} \varphi(i) i=1dnφ(i) 个。像这样枚举完之后我们总共会有 ∑ d ∈ p ⁡ ∑ i = 1 ⌊ n d ⌋ φ ( i ) \sum\limits_{d\in \operatorname{p}}\sum\limits_{i = 1}^{\lfloor\frac{n}{d}\rfloor} \varphi(i) dpi=1dnφ(i) 个取值( 这里 p ⁡ \operatorname{p} p 表示 [ 1 , n ] [1, n] [1,n] 中的质数)。

由于这道题的 x x x y y y 可以互换位置,所以对于每一个 d d d,我们可以得到的对数一共有 2 [ ∑ i = 1 ⌊ n d ⌋ φ ( i ) ] − 1 2[\sum\limits_{i = 1}^{\lfloor\frac{n}{d}\rfloor} \varphi(i)] - 1 2[i=1dnφ(i)]1 个。因此最终的答案为
∑ d ∈ p ⁡ { 2 [ ∑ i = 1 ⌊ n d ⌋ φ ( i ) ] − 1 } \sum\limits_{d\in \operatorname{p}}\{2[\sum\limits_{i = 1}^{\lfloor\frac{n}{d}\rfloor} \varphi(i)] - 1\} dp{2[i=1dnφ(i)]1}
由于我们要多次计算欧拉函数的部分和,为了快速计算它们的和,我们可以在打表完欧拉函数之后求一下前缀和。


(2) 代码实现

#include<iostream>using namespace std;typedef long long LL;const int N = 1e7 + 10;
int p[N], phi[N], cnt;
LL pref[N];
bool vis[N];// 打表欧拉函数
void get_phi(int n)
{phi[1] = 1;for(int i = 2; i <= n; i++){if(!vis[i]){p[cnt++] = i;phi[i] = i - 1;}for(int j = 0; i * p[j] <= n; j++){int x = i * p[j];vis[x] = true;if(i % p[j] == 0){phi[x] = p[j] * phi[i];break;}else{phi[x] = (p[j] - 1) * phi[i];}}}
}// 求欧拉函数表的前缀和
void pre_sum(int n)
{for(int i = 1; i <= n; i++){pref[i] = pref[i - 1] + phi[i];}
}int main()
{int n;cin >> n;get_phi(n);pre_sum(n);LL sum = 0;// 从小到大枚举质数for(int i = 0; i < cnt; i++){sum += pref[n / p[i]] * 2 - 1;}cout << sum << endl;return 0;
}
http://www.dtcms.com/a/520174.html

相关文章:

  • 【工具】Docker 的基础使用
  • 网站流量与广告费编辑wordpress文章页
  • java基础:String字符串的用法详解
  • 唐河网站制作品牌推广文案
  • VSCode/PyCharm解决“无法加载文件 ***\WindowsPowerShell\profile.ps1,因为在此系统上禁止运行脚本”
  • 做设计的需要网站下载素材吗wordpress菜单添加图标
  • HTML游戏开发:使用视频作为特效自动播放的方法
  • 单芯片USB拓展坞+百兆网卡+读卡器+100W快充芯片CH336F
  • 考研数学——一元函数微分学篇
  • MATLAB基于改进灰色聚类的装备技术风险评估方法
  • 最佳经验网站wordpress大学百度云
  • AI服务器工作之显卡测试
  • C++仿mudo库高并发服务器项目:Socket模块
  • 找人帮忙做网站吉林市百姓网免费发布信息网
  • HTTP与HTTPS协议区别及应用场景
  • HTTP 206状态码:部分内容传输核心技术
  • Vue3 -- 第一个vue项目
  • 收钱码合并的网站怎么做电商网站产品模块
  • Vitis HLS 学习指南与实践教程
  • FBH公司开发了200 MHz GaN降压变换器模块
  • SpiderDemo题解系列——第3篇:调试拦截与非对称加密挑战 — JS逆向调试实战(第23题)
  • 机器学习之数字识别
  • 网站开发群安阳网站设计多少钱
  • 7. Prometheus告警配置-alertmanger
  • 自动签到之实现掘金模拟签到
  • 【探寻C++之旅】C++11 深度解析:重塑现代 C++ 的关键特性
  • 【unity】运行时加载并修改ScriptableObject类型资源对象的值会怎样
  • Spring Boot 实现 DOCX 转 PDF(基于 docx4j 的轻量级开源方案)
  • 服装企业官方网站建设网站运营收入
  • Spring Boot Actuator深度解析与实战