数学/数论专题-专项训练:欧拉函数
1. 前言
本篇博文是欧拉函数的专项训练。
其实一般数论的题目就是推式子难,式子推出来了代码都好打。
如果您没有学过欧拉函数,可以看一看我的这篇博文:数论专题-学习笔记:欧拉函数
这里放一下欧拉函数的 8 个性质:
- 基本性质 1:若 为质数,那么 。特别的,。
- 基本性质 2:设 且 为质数,那么:
- 基本性质 3:欧拉函数是积性函数。
- 基本性质 4:对于数 ,将其质因数分解为 ,那么:
- 扩展性质 1:设 ,那么 。
- 扩展性质 2:。
- 扩展性质 3:设 为质数,那么:
- 扩展性质 4:对于一个数 (),所有小于 且与 互质的数的和为 。
读者应当对以上内容足够了解且能够独立证明,如不能也可以到上面的博文里面查看证明过程。
那么接下来就开始愉快的推式子吧~
2. 练习题
题单:
P2568 GCD
设 为质数集合。
先将题中要求的东西写出来:
显然 ,于是可以将 往前移一下:
( 在这里表示当 为真时其值为 1,否则为 0)
然后根据 的性质转化一下后面两个求和符号:
然后会发现,,于是乎我们可以再对式子做一个转化:
然后可以发现,如果 ,那么这个在 与 中都会出现,也就是互质数对出现 2 次。
那么统计一下 就可以了(减 1 是因为 会被统计两次),也就是这样:
然后 处理一下 ,做个前缀和就好了。
代码:
/*
========= 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
法一:
首先先转换一下式子:
然后根据 的性质,稍微转换一下式子:
然后将 移到前面,同时改变一下枚举顺序:
等等,后面这个不就是上面这道题吗?
套用上面这道题的方法,就可以将式子转成这个:
然后做一遍前缀和+线性筛就可以了。
代码:
/*
========= 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:。
于是式子就可以转化成这样:
考虑到 的充要条件是 ,那么式子就可以转化成这样:
再转化一下式子:
提前枚举 的求和符号:
而 ,那么最后答案就是:
于是线性筛求一遍 ,然后直接计算即可。
代码见上面的注释部分。
3. 总结
数论题只需要推出式子,代码就能够写出来。
这次的两道题都是关于 的,而关于 的处理方法通常是转化求和符号,转化为形如 这样的互质形式,然后采用欧拉函数求解。
当然关于 的问题更加通用的办法是莫比乌斯反演。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具