欧拉函数与欧拉定理
欧拉函数
定义
欧拉函数,即
当
性质
性质1
欧拉函数是积性函数。
积性函数:积性函数是指对于所有互质的整数
和 都有性质 的数论函数,例如欧拉函数 ,莫比乌斯函数 ,因数个数函数 。
完全积性函数:对于任意整数和 有性质 的数论函数。
积性函数的性质:若和 都是积性函数,那么以下函数也为积性函数:
, , , 。
对于欧拉函数,如果有
特别地,当
性质2
此性质为欧拉反演的定义。
证明:
显然对于一个
将公式变形,得到:
发现内层循环就是求在
由于
证毕。
性质3
若
证明:相当于找在
性质4
由整数的惟一分解定理,设
惟一分解定理有多种指代意义:整数惟一分解定理,多项式惟一分解定理,交的惟一分解定理,乘积的惟一分解定理。
整数的惟一分解定理,又称算术分解定理,是数论的重要理论之一。该定理可以表述为:对于任意一个大于的整数 都可以分解为若干个素因数的连乘积,如果不计各个素因数的顺序,那么这种分解是唯一的。
即若有,则有 ,其中 为素数, 为正整数。
证明:由整数的惟一分解定理与性质
求欧拉函数
求单个数的欧拉函数值
例题:SP4141 ETF - Euler Totient Function
根据性质
纷总总兮九州,何寿天兮在予。
#include <bits/stdc++.h> using namespace std; int T,n,ans; int main(){ scanf("%d",&T);while(T --){ scanf("%d",&n);ans = n; for(int i = 2;i * i <= n;i ++){ if(n % i == 0){ ans = ans / i * (i - 1); while(n % i == 0) n /= i; } } if(n > 1) ans = ans / n * (n - 1); printf("%d\n",ans); } return 0; }
线性筛法求多个数的欧拉函数值
我们先回顾一下线性筛法筛质数。我们筛质数的思路即是,保证每一个合数都只被筛到一次,并且只被它的最小质因子筛到:
愿世间不再有无归之人。
void getPrime(){ int cnt = 0;vis[1] = 1; for(int i = 2;i <= n;i ++){ if(vis[i] == 0) Prime[++cnt] = i; for(int j = 1;j <= cnt and i * Prime[j] <= n;j ++){ vis[i * Prime[j]] = 1; if(i % Prime[j] == 0) break; } } }
那么在线性求欧拉函数的得时候,基本框架还是上面的东西,不过我们需要另外处理一些东西:
我们令
如果说
否则,此时
司命者,当无情,有情即孽。
void getPhi(){ int cnt = 0;vis[1] = 1;phi[1] = 1; for(int i = 2;i <= n;i ++){ if(vis[i] == 0) Prime[++cnt] = i,phi[i] = i - 1; for(int j = 1;j <= cnt and i * Prime[j] <= n;j ++){ vis[i * Prime[j]] = 1; if(i % Prime[j] == 0){ phi[i * Prime[j]] = phi[i] * Prime[j]; break; } phi[i * Prime[j]] = phi[i] * phi[Prime[j]];//or phi[i] * (Prime[j] - 1) } } }
习题
POJ 2478
线性求欧拉函数板子题。
跨过忘忧渡口,从此生死两隔。
#include <iostream> #include <cstdio> #define N 1000006 #define int long long using namespace std; int n,vis[N],phi[N],Prime[N],cnt; void getPhi(){ vis[1] = 1; for(int i = 2;i <= 1000000;i ++){ if(!vis[i]) phi[i] = i - 1,Prime[++cnt] = i; for(int j = 1;j <= cnt && Prime[j] * i <= 1000000;j ++){ vis[i * Prime[j]] = 1; if(i % Prime[j] == 0){ phi[i * Prime[j]] = phi[i] * Prime[j]; break; } phi[i * Prime[j]] = phi[i] * (Prime[j] - 1); } } for(int i = 2;i <= 1000000;i ++) phi[i] += phi[i - 1]; } signed main(){ getPhi();while(cin >> n){ if(n == 0) return 0; printf("%lld\n",phi[n]); } }
P2158 [SDOI2008] 仪仗队
容易发下左上角可右下角是对称的,因此我们劈开一半只看右下方。
我们以 C 君的位置为原点建立平面直角坐标系。容易得到结论,当且仅当
忘忧沼泽,是所有生命的归处,既结束,亦开始。
#include <bits/stdc++.h> #define N 40005 using namespace std; int n,vis[N],phi[N],cnt,num,Prime[N]; void Getphi(){ int cnt = 0;vis[1] = 1,phi[1] = 1,num = 1; for(int i = 2;i <= n - 1;i ++){ if(vis[i] == 0) Prime[++cnt] = i,phi[i] = i - 1; num += phi[i]; for(int j = 1;j <= cnt and Prime[j] * i <= n;j ++){ vis[i * Prime[j]] = 1; if(i % Prime[j] == 0){ phi[i * Prime[j]] = phi[i] * Prime[j]; break; } phi[i * Prime[j]] = phi[i] * (Prime[j] - 1); } } } int main(){ scanf("%d",&n);Getphi(); if(n == 1) printf("0"); else printf("%d",num * 2 + 1); return 0; }
P2568 GCD
思路很明显,我们先利用线性求欧拉函数计算出
烈阳灼身,寒风刺骨,皆是无归之痛。
#include <bits/stdc++.h> #define N 10000007 #define int long long using namespace std; int n,num,cnt,vis[N],phi[N],Prime[N]; void getPhi(){ vis[1] = 1,phi[1] = 1; for(int i = 2;i <= n;i ++){ if(vis[i] == 0) Prime[++cnt] = i,phi[i] = i - 1; for(int j = 1;j <= cnt and Prime[j] * i <= n;j ++){ vis[i * Prime[j]] = 1; if(i % Prime[j] == 0){ phi[i * Prime[j]] = phi[i] * Prime[j]; break; } phi[i * Prime[j]] = phi[i] * (Prime[j] - 1); } } } signed main(){ scanf("%lld",&n);getPhi(); for(int i = 2;i <= n;i ++) num += (upper_bound(Prime + 1,Prime + cnt + 1,n / i) - Prime - 1) * phi[i]; printf("%lld",num * 2 + cnt); return 0; }
P2303 [SDOI2012] Longge 的问题
本题即为欧拉反演,即性质
推导:
令答案为
观察到,若
将内层循环移至外层,得到
易得,在
推导毕。
时间复杂度分析:
我们枚举因数的时间复杂度是
根据惟一分解定理,我们对
当
因此因数个数为
天地之间,无法消散的执念,便化作恶灵。
#include <bits/stdc++.h> #define int long long using namespace std; int n,num; int getPhi(int x){ int ans = x; for(int i = 2;i * i <= x;i ++){ if(x % i == 0){ ans = ans / i * (i - 1); while(x % i == 0) x /= i; } } if(x > 1) ans = ans / x * (x - 1); return ans; } void Decom(){ for(int i = 1;i * i <= n;i ++){ if(n % i == 0){ if(i * i == n) num += i * getPhi(i); else num += (n / i) * getPhi(i) + i * getPhi(n / i); } } } signed main(){ scanf("%lld",&n);Decom(); printf("%lld",num); return 0; }
P2398 GCD SUM
本题只不过是上一个题稍微进阶一下,可以尝试自己推一下式子。
易得,原式
推导毕。
云梦之人相信,人死之后,会魂归天生木,再次归家。
#include <bits/stdc++.h> #define N 100005 #define int long long using namespace std; int n,num,cnt,vis[N],phi[N],Prime[N]; void getPhi(){ vis[1] = 1,phi[1] = 1,num = n * n; for(int i = 2;i <= n;i ++){ if(vis[i] == 0) Prime[++cnt] = i,phi[i] = i - 1; num += phi[i] * (n / i) * (n / i); for(int j = 1;j <= cnt and Prime[j] * i <= n;j ++){ vis[i * Prime[j]] = 1; if(i % Prime[j] == 0){ phi[i * Prime[j]] = phi[i] * Prime[j]; break; } phi[i * Prime[j]] = phi[i] * (Prime[j] - 1); } } } signed main(){ scanf("%lld",&n);getPhi(); printf("%lld",num); return 0; }
P2350 [HAOI2012] 外星人
本题是对性质
我们知道,在质数中只有
显然,如果
但是我们要注意一个细节问题:如果刚开始这个数
那么,除了上面的特例外,其他情况下,每一次操作一定会减少一个
回首万里,故人长绝。
#include <bits/stdc++.h> #define N 100005 #define int long long using namespace std; int T,num,f[N],vis[N],Prime[N],cnt,m; void getThrough(){ f[1] = 1,vis[1] = 1; for(int i = 2;i <= 100000;i ++){ if(!vis[i]) f[i] = f[i - 1],Prime[++cnt] = i; for(int j = 1;j <= cnt and Prime[j] * i <= 100000;j ++){ vis[i * Prime[j]] = 1; f[i * Prime[j]] = f[i] + f[Prime[j]]; if(i % Prime[j] == 0) break; } } } signed main(){ getThrough();scanf("%lld",&T);while(T --){ scanf("%lld",&m);num = 1; for(int i = 1,p,q;i <= m;i ++){ scanf("%lld%lld",&p,&q);if(p == 2) num --; num += f[p] * q; } printf("%lld\n",num); } return 0; }
欧拉定理
前置 1 简化剩余系的概念
剩余类
定义
剩余类,亦称同余类。设模为
形式化的,即以
性质
完全剩余系
定义
从模
举例:模
性质
证明
使用反证法。假设存在,则 。
, , 。
但是,因此假设不成立。因此 两两不同余, 后显然成立。
证毕。
证明
依旧使用反证法。假设存在。
则,显然有 。
, ,即 。同理可证得 。
但是二者均不满足条件,因此假设不成立。因此集合内元素两两不同余。
证毕。
简化剩余系
定义
简化剩余系也称既约剩余系或缩系,是
举例:
性质
证明比较显然。
,因此 ,加上 后同理。证毕。
前置 2 关于费马小定理的证明
费马小定理:若
证明:
设一个质数为
构造一个序列
那么根据完全剩余系的性质
我们设
证毕。
定义
若
证明:
与费马小定理类似。同样构造一个与
设
因此
因此在满足
证毕。
然后我们可以通过这个结论证明费马小定理。由于当
扩展欧拉定理
定义
证明不会。
例题 P5091 【模板】扩展欧拉定理
一个很重要的步骤就是边输入边取模。
于荒林中独行夜路,就算心中志忑,也不必回头。
#include <bits/stdc++.h> #define int long long using namespace std; int a,m,b,phi,flag; inline int read() { int x = 0,f = 1;char ch = getchar(); while(ch < '0' or ch > '9'){ if (ch == '-') f = -1; ch = getchar(); } while(ch >= '0' and ch <= '9'){ x = x * 10 + ch - 48; if(x >= phi) flag = 1; x %= phi;ch = getchar(); } return x * f; } int ksm(int a,int b){ int res = 1; while(b){ if(b & 1) res = res * a % m; a = a * a % m; b >>= 1; } return res; } int getPhi(int n){ int ans = n; for(int i = 2;i * i <= n;i ++){ if(n % i == 0){ ans = ans / i * (i - 1); while(n % i == 0) n /= i; } } if(n > 1) ans = ans / n * (n - 1); return ans; } signed main(){ scanf("%lld%lld",&a,&m);a %= m; phi = getPhi(m);b = read(); b += (flag == 1 ? phi : 0); printf("%lld",ksm(a,b)); return 0; }
习题
P5221 Product
原式
后者的指数我们可以使用欧拉函数的前缀和解决,但是空间
若要终结枯灾,唯有九神巫同补天穹。
#include <bits/stdc++.h> #define N 1000006 #define ll long long #define MOD 104857601 using namespace std; bool vis[N]; ll ans = 1,num = 1,sum; int n,cnt,phi[N],Prime[80004]; void getPhi(){ vis[1] = 1,phi[1] = 1; for(int i = 2;i <= n;i ++){ if(!vis[i]) Prime[++cnt] = i,phi[i] = i - 1; for(int j = 1;j <= cnt and Prime[j] * i <= n;j ++){ vis[Prime[j] * i] = 1; if(i % Prime[j] == 0){ phi[i * Prime[j]] = phi[i] * Prime[j]; break; } phi[i * Prime[j]] = phi[i] * (Prime[j] - 1); } } for(int i = 2;i <= n;i ++) phi[i] = (phi[i] * 2 + phi[i - 1]) % (MOD - 1); } ll ksm(ll a,ll b){ ll res = 1; while(b){ if(b & 1) res = res * a % MOD; a = a * a % MOD; b >>= 1; } return res; } signed main(){ scanf("%d",&n);getPhi(); for(int i = 1;i <= n;i ++){ ans = ans * i % MOD; num = num * ksm(i,phi[n / i]) % MOD; }num = num * num % MOD; sum = ksm(ans,2 * n) * ksm(num,MOD - 2) % MOD; printf("%lld",sum); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效