数学/数论专题-专项训练:欧拉函数
1. 前言
本篇博文是欧拉函数的专项训练。
其实一般数论的题目就是推式子难,式子推出来了代码都好打。
如果您没有学过欧拉函数,可以看一看我的这篇博文:数论专题-学习笔记:欧拉函数
这里放一下欧拉函数的 8 个性质:
- 基本性质 1:若 \(p\) 为质数,那么 \(\varphi(p)=p-1\)。特别的,\(\varphi(1)=1\)。
- 基本性质 2:设 \(n = p^k\) 且 \(p\) 为质数,那么:\[\varphi(n)=n-\dfrac{n}{k}=n-p^{k-1}=p^{k-1} \times (p-1)=p^{k-1} \times \varphi(p) \]
- 基本性质 3:欧拉函数是积性函数。
- 基本性质 4:对于数 \(n\),将其质因数分解为 \(\prod_{i=1}^{k}{p_i}^{r_i}\),那么:\[\varphi(n)=\prod_{i=1}^{k}\varphi(p_i^{r_i})=\prod_{i=1}^{k}(p_i^{r_i-1} \times (p_i-1))=n \times \prod_{i=1}^{k}(1-\dfrac{1}{p_i}) \]
- 扩展性质 1:设 \(n=a \times b,\gcd(a,b) = d \in N_+\),那么 \(\varphi(n)=\dfrac{\varphi(a) \times \varphi(b) \times d}{\varphi(d)}\)。
- 扩展性质 2:\(\forall n \in N_+,n=\sum\limits_{d|n}{\varphi(d)}\)。
- 扩展性质 3:设 \(n \in N_+,p\) 为质数,那么:\[\varphi(n \times p)=\begin{cases}\varphi(n) \times \varphi(p)&p \nmid n\\\varphi(n) \times p&p \mid n\end{cases} \]
- 扩展性质 4:对于一个数 \(n\)(\(n \geq 2\)),所有小于 \(n\) 且与 \(n\) 互质的数的和为 \(\varphi(n) \times \dfrac{n}{2}\)。
读者应当对以上内容足够了解且能够独立证明,如不能也可以到上面的博文里面查看证明过程。
那么接下来就开始愉快的推式子吧~
2. 练习题
题单:
P2568 GCD
设 \(Prime\) 为质数集合。
先将题中要求的东西写出来:
显然 \(k \in [1,n]\),于是可以将 \(k\) 往前移一下:
(\([A]\) 在这里表示当 \(A\) 为真时其值为 1,否则为 0)
然后根据 \(\gcd\) 的性质转化一下后面两个求和符号:
然后会发现,\(\dfrac{i}{k} \in [1,\left\lfloor\dfrac{n}{k}\right\rfloor]\),于是乎我们可以再对式子做一个转化:
然后可以发现,如果 \(\gcd(i,j)=1\),那么这个在 \(i=i,j=j\) 与 \(i=j,j=i\) 中都会出现,也就是互质数对出现 2 次。
那么统计一下 \(2\sum\limits_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\varphi(i)-1\) 就可以了(减 1 是因为 \((1,1)\) 会被统计两次),也就是这样:
然后 \(O(n)\) 处理一下 \(\varphi(i)\),做个前缀和就好了。
代码:
/*
========= Plozia =========
Author:Plozia
Problem:P2568 GCD
Date:2021/4/6
========= Plozia =========
*/
#include <bits/stdc++.h>
using std::vector;
typedef long long LL;
const int MAXN = 1e7 + 10;
int n, phi[MAXN];
bool book[MAXN];
LL ans = 0, sum[MAXN];
vector <int> v;
int read()
{
int sum = 0, fh = 1; char ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
return sum * fh;
}
int main()
{
n = read(); book[1] = 1; sum[1] = phi[1] = 1;
for (int i = 2; i <= n; ++i)
{
if (!book[i]) {phi[i] = i - 1; v.push_back(i);}
sum[i] = sum[i - 1] + (LL)phi[i];
for (int j = 0; j < v.size(); ++j)
{
if ((LL)i * v[j] > n) break;
book[i * v[j]] = 1;
if (i % v[j] == 0) {phi[i * v[j]] = phi[i] * v[j]; break;}
phi[i * v[j]] = phi[i] * (v[j] - 1);
}
}
for (int i = 0; i < v.size(); ++i) ans += (sum[n / v[i]] << 1) - 1;
printf("%lld\n", ans); return 0;
}
P2398 GCD SUM
法一:
首先先转换一下式子:
然后根据 \(\gcd\) 的性质,稍微转换一下式子:
然后将 \(d\) 移到前面,同时改变一下枚举顺序:
等等,后面这个不就是上面这道题吗?
套用上面这道题的方法,就可以将式子转成这个:
然后做一遍前缀和+线性筛就可以了。
代码:
/*
========= Plozia =========
Author:Plozia
Problem:P2398 GCD SUM
Date:2021/4/6
========= Plozia =========
*/
#include <bits/stdc++.h>
using std::vector;
typedef long long LL;
const int MAXN = 1e5 + 10;
int n, phi[MAXN];
bool book[MAXN];
LL ans = 0, sum[MAXN];
vector <int> v;
int read()
{
int sum = 0, fh = 1; char ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
return sum * fh;
}
int main()
{
n = read(); book[1] = 1; phi[1] = 1; sum[1] = 1;
for (int i = 2; i <= n; ++i)
{
if (!book[i]) {phi[i] = i - 1; v.push_back(i);}
sum[i] = sum[i - 1] + (LL)phi[i];
for (int j = 0; j < v.size(); ++j)
{
if (i * v[j] > n) break;
book[i * v[j]] = 1;
if (i % v[j] == 0) {phi[i * v[j]] = phi[i] * v[j]; break;}
phi[i * v[j]] = phi[i] * (v[j] - 1);
}
}
//做法 2
// for (int i = 1; i <= n; ++i)
// ans += (LL)phi[i] * (LL)(n / i) * (LL)(n / i);
// printf("%lld\n", ans);
//做法 1
for (int i = 1; i <= n; ++i)
ans += i * (2ll * sum[n / i] - 1);
printf("%lld\n", ans);
return 0;
}
法二:
记得扩展性质 2 吗?
- 扩展性质 2:\(\forall n \in N_+,n=\sum\limits_{d|n}{\varphi(d)}\)。
于是式子就可以转化成这样:
考虑到 \(d \mid \gcd(i,j)\) 的充要条件是 \(d \mid i,d \mid j\),那么式子就可以转化成这样:
再转化一下式子:
提前枚举 \(d\) 的求和符号:
而 \(\sum_{i=1}^{n}\sum_{j=1}^{n}([d \mid i] \times [d \mid j]) = \left\lfloor\frac{n}{d}\right\rfloor^2\),那么最后答案就是:
于是线性筛求一遍 \(\varphi(d)\),然后直接计算即可。
代码见上面的注释部分。
3. 总结
数论题只需要推出式子,代码就能够写出来。
这次的两道题都是关于 \(\gcd\) 的,而关于 \(\gcd\) 的处理方法通常是转化求和符号,转化为形如 \(\gcd(a,b)=1\) 这样的互质形式,然后采用欧拉函数求解。
当然关于 \(\gcd\) 的问题更加通用的办法是莫比乌斯反演。

浙公网安备 33010602011771号