欧拉定理 & 扩展欧拉定理 笔记
欧拉函数
欧拉函数定义为: 表示 中所有与 互质的数的个数。
关于欧拉函数有下面的性质和用途:
-
欧拉函数是积性函数。可以通过这个性质求出他的公式。
-
。很显然,比质数 小的所有数都与他互质。
-
。显然,对于这 个数,只有 不与 互质。
-
。
-
假设 ,则 。
-
-
欧拉函数可以用线性筛筛出来。
假设当前的数是 ,遍历到的质数为 ,那么 肯定要被筛掉。根据欧拉筛:
-
若 ,那么 是 的最小质因子,且 中包含 的所有质因子。根据单个欧拉函数的求法可以得到:。
-
否则, 和 互质,根据积性函数的性质,。
-
int get_phi(int n) {
phi[1] = 1;
for (int i = 2; i <= n; i ++ ) {
if (!st[i]) p[ ++ cnt] = i, phi[i] = i - 1;
for (int j = 1; j <= cnt and 1ll * i * p[j] <= n; j ++ ) {
st[i * p[j]] = true; if (i % p[j] == 0) {
phi[i * p[j]] = phi[i] * p[j]; break;
} phi[i * p[j]] = phi[i] * phi[p[j]];
}
}
}
- 欧拉函数可以用来降幂。下面就来介绍。
完全剩余系
对于整数集 满足:
-
任意不同元素 ,都有 。
-
,。
例如,对于 , 就是 的一个完全剩余系。通常地,将模 的完全剩余系记做 。
实际应用中,通常用 来表示,也就是模 的最小非负完全剩余系。
推论 :任意 个连续整数构成模 的完全剩余系。
推论 :若 且 是一个完全剩余系,则 构成一个完全剩余系。
下面证明推论 。
设集合 。对于 。假设存在同余 。由于 ,因此 。根据完全剩余系的互异性,假设不成立。 的互异性证明完成。
因为 ,由定义 可得,, 中有唯一的 满足 。因此构成 对 的单射。又因为 ,故构成 到 的双射。
则对于任意整数,都存在 中的某个数 与之同余,亦存在 中某个数与之同余。定义 证明完成。因此 是一个完全剩余系。
简化剩余系
在完全剩余系基础上加上了更强的限制。
定义 为模 的简化剩余系,当且仅当:
-
任意不同元素 ,都有 。
-
,。
-
。
其中定义 可以合并为 ,都存在唯一的 满足 。
下面证明简化剩余系的一些推论。
推论 :该剩余系对于 的乘法具有封闭性。
证明:,都有 。因此有 。根据性质 , 也在 中。
根据 可知, 的乘法逆元也在 中。因此证明了关于乘法和除法的封闭性,还证明了逆元的存在。
事实上,该简化剩余系 构成关于模 乘法运算的交换群(阿贝尔群)。
推论 :。
推论 :对于 ,有:
证明:可能算是感性证明。根据欧几里得算法的流程,,则 ,也即 。因此,如果 在 中,则 也在 中。由于 与 模 不相等,因此简化剩余系中的数总是成对出现。相加可以证明。
这也说明,简化剩余系 的大小 一定是偶数()。
推论 :若 且 是一个完全剩余系,则 构成一个完全剩余系。
证明方法可以参考完全剩余系性质 的证明。
欧拉定理
定义:若 均为正整数,且 与 互质,则有 。
证明:对于 的简化剩余系 ,根据简化剩余系的推论 可知, 也构成模 的简化剩余系。
根据 ,有 。证毕。
由此可以得到更弱的定理 Farmet 小定理:,。
扩展欧拉定理
扩展欧拉定理的证明就不在我能力所及的范围内了。在这就放个结论吧。
这个柿子就比较有用了,可以用来搞降幂之类的事情。
例题
求 的值。
设 表示 的幂塔对 取模的值。那么他就等于 。由于 函数下降速度极快( 速度),很快 降为 。这个东西可以递归求解。
int solve(int p) {
if (p == 1) return 0;
int phi = get_phi(p);
int s = solve(phi);
return qpow(2, s + phi, p);
}
给定一个数列 和模数 , 每次询问一个区间 ,求 的值
考虑欧拉降幂。欧拉函数下降速度很快, 次就能降为 。因此可能只需要计算到 ,后面的就全模 了(当然模 就是 )。这个 是 级别的。
上面讨论的都是幂次大于 的情况。如果幂次小于 呢?怎么提前判断掉呢?
如果数列的每一项都 ,那么可以直接暴力判断,因为 个数乘起来就会达到 。但是如果存在 呢?这个也好办,直接把 调到 后边的第一个 前面就行,这样保证了 中没有 。同时,这个 对答案也没有影响,因为 。
下面是一份可供参考的代码实现:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <map>
#define int long long
using namespace std;
const int N = 100010;
int n, m, p, a[N], suf[N];
map<int, int> bin;
int qpow(int a, int b, int p) {
int s = 1; for (; b; b >>= 1, a = a * a % p)
if (b & 1) s = s * a % p; return s;
}
bool check(int a, int b, int p) {
int s = 1;
for (; b; b >>= 1, a = a * a) {
if (a >= p) return 0;
if (b & 1) {
s = s * a; if (s >= p) return 0;
}
} return 1;
}
int get_phi(int n) {
int s = n;
for (int i = 2; i <= n / i; i ++ ) if (!(n % i)) {
s = s / i * (i - 1); while (!(n % i)) n /= i;
} if (n != 1) s = s / n * (n - 1); return s;
}
int solve(int u, int r, int p) {
if (u == r) return a[u] % p;
if (p == 1) return 0;
int phi = bin[p], s = a[r]; bool flg = 0;
for (int i = r; i > u + 1; i -- ) {
if (!check(a[i - 1], s, phi)) { flg = 1; break; }
s = qpow(a[i - 1], s, phi);
} if (s >= phi) flg = 1; int pw;
if (flg) pw = solve(u + 1, r, phi) + phi;
else pw = s;
return qpow(a[u], pw, p);
}
signed main() {
scanf("%lld%lld", &n, &p);
for (int i = 1; i <= n; i ++ )
scanf("%lld", &a[i]);
scanf("%lld", &m); int t = p; bin[1] = 1;
while (t != 1) bin[t] = get_phi(t), t = bin[t];
suf[n + 1] = n + 1;
for (int i = n; i >= 1; i -- )
if (a[i] == 1) suf[i] = i;
else suf[i] = suf[i + 1];
while (m -- ) {
int l, r; scanf("%lld%lld", &l, &r);
r = min(r, suf[l]); printf("%lld\n", solve(l, r, p));
} return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示