「欧拉函数」学习笔记
前知导入
唯一分解定理
对于任何一个大于
即若
其中,
容斥原理
在此仅做简单引入
带入情景,班里有
个同学喜欢数学, 个喜欢语文, 个喜欢英语,求至少喜欢一门的学生个数
这个问题很好解决,讲喜欢这三科的学生的集合分别记作
则
这就是简单的容斥原理
欧拉函数定义
写作
通式
-
:当
时,根据唯一分解定理,设 其中 为 的质因子则
中, 的倍数有 个, 的倍数有 个根据容斥原理,
中不与 有共同因子 或 的个数为以此类推,将
的全部质因子全部代入容斥原理,即可得到
-
-
线性筛法求
的欧拉函数时间复杂度
void euler(int n) { phi[1]=1; for(int i=2;i<=n;i++) { if(!vis[i]) len++, prime[len]=i, phi[i]=i-1; for(int j=1;j<=len&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(!(i%prime[j])) { phi[i*prime[j]]=phi[i]*prime[j]; break; } else phi[i*prime[j]]=phi[i]*(prime[j]-1); } } }
-
求单个数的欧拉函数
先对其分解质因数,然后带入公式
单个数的复杂度为
int phi(int x) { int ret=x; for(int i=2;i<=sqrt(n);i++) if(x%i==0) { ret=ret/i*(i-1); while(x%i==0) x/=i; } if(x>1) ret=ret/x*(x-1); return ret; } -
性质
- 当
为质数时,- 显然
-
欧拉函数是积性函数
-
当
时, -
由此不难推出,当
为奇数时, -
证明:
根据唯一分解定理,不妨设
因为
,所以一定不存在一组那么
可分解为 ,其中 一定不相等故此,依据性质
,则有又因为
所以得到
-
- 欧拉反演
- 根据约数
的对称性
- 根据约数
- 若
且 为质数,那么- 根据定义可得
-
若
为正整数,则满足- 但在这里显然,当
时下面那个式子是不满足的,因此通式可以写成
- 但在这里显然,当
相关知识扩展
欧拉定理
若正整数
- 在此只做描述,
暂时未学费马小定理,不会证
扩展欧拉定理
例题
1. 仪仗队
-
如图:
这是一个正方形,不放将他看做一个三角形,再乘以
以左下角点为原点
建立坐标系设一点
,当 互质时,即 时,他可以被看到反之,则一定有一点
将他挡住问题可转化为:
中分别与 互质的个数也就是求
因为欧拉函数求的是
中与 互质的个数,但这里是从 开始的,所以纵坐标是 的这一条线上 是可以看到的,后面都挡住了,所以要 ; 显然,因为我们是将他看做一个三角形去做的注意事项: 是
而不是 ,因为我们的第一列在坐标轴中视作 了
#include<bits/stdc++.h> #define int long long #define endl '\n' using namespace std; const int N=4e4+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=1; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,len,ans,phi[N],prime[N],vis[N]; void euler(int n) { phi[1]=1; for(int i=2;i<=n;i++) { if(!vis[i]) len++, prime[len]=i, phi[i]=i-1; for(int j=1;j<=len&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(!(i%prime[j])) { phi[i*prime[j]]=phi[i]*prime[j]; break; } else phi[i*prime[j]]=phi[i]*(prime[j]-1); } } } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n); euler(n); for(int i=1;i<n;i++) ans+=phi[i]; cout<<(ans<<1)+1; } - 这里采用筛法求
的欧拉函数,复杂度 ,因为要求 的欧拉函数的和
- 这里采用筛法求
2. 的问题
#include<bits/stdc++.h> #define int long long #define endl '\n' using namespace std; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=1; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,len,ans; int phi(int x) { int ret=x; for(int i=2;i<=sqrt(n);i++) if(x%i==0) { ret=ret/i*(i-1); while(x%i==0) x/=i; } if(x>1) ret=ret/x*(x-1); return ret; } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n); for(int i=1;i<=sqrt(n);i++) if(n%i==0) if(i*i!=n) ans+=i*phi(n/i),ans+=(n/i)*phi(i); else ans+=i*phi(i); cout<<ans; } - 根据
的对称性,只需要 即可
- 根据
3. 公主的诱惑
-
多组测试数据,所以要先预处理(递推||筛法),不然显然会超时
先设一个
,即数据范围,输入中给了总的根据上面推出的式子,需要先处理 质数,阶乘,乘法逆元,
- 质数是为了处理
的 - 阶乘显然是为了处理
的,别忘了 - 至于乘法逆元,因为在
意义下,直接除以 是不行的,要用到 - 之后就可以处理
这样对于每一组输入,直接
输出即可 - 质数是为了处理
-
当
中存在整除 时,式子出来就是 了,但显然不应该是对此,当循环到
时,将 中的 因子全部消掉,即int x=i; while(x%p==0) x/=p; 在下面的代码中有体现
同时,当
且 时,答案显然是 ,需特判一下if(n>=p&&m<p) puts("0");
#include<bits/stdc++.h> #define int long long #define endl '\n' using namespace std; const int N=1e7+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=1; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int t,p,n,m,jc[N],ans[N],inv[N]; bool pri[N]; void niyuan() { inv[1]=1;if(n>=p&&m<p) puts("0"); for(int i=2;i<N;i++) { int x=i; while(x%p==0) x/=p; inv[i]=((p-p/x)*inv[p%x])%p; } } void prime() { for(int i=2;i<sqrt(N);i++) if(!pri[i]) for(int j=2;i*j<N;j++) pri[i*j]=1; } void prod() { jc[1]=1; for(int i=2;i<N;i++) { int x=i; while(x%p==0) x/=p; jc[i]=(jc[i-1]*x)%p; } } void solve() { ans[1]=1; for(int i=2;i<N;i++) if(!pri[i]) { int x=i,y=i-1; while(x%p==0) x/=p; while(y%p==0) y/=p; ans[i]=(ans[i-1]*y)%p,ans[i]=(ans[i]*inv[x])%p; } else ans[i]=ans[i-1]; } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(t),read(p); niyuan(),prime(),prod(),solve(); while(t--) { read(n),read(m); if(n>=p&&m<p) puts("0"); else cout<<(jc[n]*ans[m])%p<<endl; } } - 此处注意数组开的是
,预处理的时候不能处理到 而应是 ,不然会炸数组
- 此处注意数组开的是
4.
5. 能量采集
-
求
看起来十分眼熟,和第
道 是十分像的,借鉴那道题,则这道题的式子为:但是仔细阅读题面,他并不是上面的式子,拿
打比方,他前面有 三个点,所以该点的能量损失为 ,而 所以要求的实际上应该是那么简单转化一下即可,即
6. 公约数的和
7.
8. 疯狂
-
多组测试数据,需要预处理
欧拉函数筛法预处理,之后在线性预处理
,之后 输出即可预处理代码如下:
void euler(int n) { phi[1]=1; for(int i=2;i<=n;i++) { if(!vis[i]) len++, prime[len]=i, phi[i]=i-1; for(int j=1;j<=len&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(!(i%prime[j])) { phi[i*prime[j]]=phi[i]*prime[j]; break; } else phi[i*prime[j]]=phi[i]*(prime[j]-1); } } } void solve(int n) { for(int i=1;i<=n;i++) for(int j=i;j<=n;j+=i) ans[j]+=(phi[i]*i+1)>>1; } 至于
的那一部分预处理之前没有用过,新学一下根据式子
,带入我们的代码,定满足 ,因为是
#include<bits/stdc++.h> #define int long long #define endl '\n' using namespace std; const int N=1e6+1; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=1; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int t,n,len,ans[N],prime[N],phi[N]; bool vis[N]; void euler(int n) { phi[1]=1; for(int i=2;i<=n;i++) { if(!vis[i]) len++, prime[len]=i, phi[i]=i-1; for(int j=1;j<=len&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(!(i%prime[j])) { phi[i*prime[j]]=phi[i]*prime[j]; break; } else phi[i*prime[j]]=phi[i]*(prime[j]-1); } } } void solve(int n) { for(int i=1;i<=n;i++) for(int j=i;j<=n;j+=i) ans[j]+=(phi[i]*i+1)>>1; } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif euler(N); solve(N); read(t); while(t--) read(n), cout<<ans[n]*n<<endl; }
9.
-
注意事项
预处理的时候,数组开的是
,循环到 就炸了!,所以预处理到不知道第几次因为这个WA了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效