漫谈剩余系理论 & 欧拉定理 & 扩展欧拉定理
Link_Cut_Y · · 算法·理论
欧拉函数
欧拉函数定义为:
\varphi(n) 表示1 \sim n 中所有与n 互质的数的个数。
关于欧拉函数有下面的性质和用途:
-
欧拉函数是积性函数。可以通过这个性质求出他的公式。
-
-
-
-
假设
n = \prod^k p_i ^ {c_i} ,则f(n) = \prod^k f(p_i ^ {c_i}) = \prod^k p_k ^ {c_k - 1}(p_k - 1) = n \prod^k \dfrac{p_k - 1}{p_k} 。
-
-
欧拉函数可以用线性筛筛出来。
假设当前的数是
n ,遍历到的质数为p ,那么m = p \times n 肯定要被筛掉。根据欧拉筛:-
若
p | n ,那么p 是n 的最小质因子,且n 中包含m 的所有质因子。根据单个欧拉函数的求法可以得到:\varphi(m) = \varphi(n) \times p 。 -
否则,
p 和n 互质,根据积性函数的性质,\varphi(m) = \varphi(n) \varphi(p) 。
-
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]];
}
}
}
- 欧拉函数可以用来降幂。下面就来介绍。
完全剩余系
对于整数集
-
任意不同元素
r_i, r_j ,都有r_i \not \equiv r_j(\bmod \ m) 。 -
例如,对于
实际应用中,通常用
推论
1 :任意m 个连续整数构成模m 的完全剩余系。推论
2 :若a \bot m 且\mathbb{Z_m} 是一个完全剩余系,则S = \{a, 2a \cdots ma\} 构成一个完全剩余系。
下面证明推论
设集合
因为
则对于任意整数,都存在
简化剩余系
在完全剩余系基础上加上了更强的限制。
定义
-
任意不同元素
r_i, r_j \in \Phi_m ,都有r_i \not \equiv r_j(\bmod \ m) 。 -
-
其中定义 $(2), (3)$ 可以合并为 $\forall a \bot m$,都存在唯一的 $r \in \Phi_m$ 满足 $a \equiv r(\bmod \ m)$。 下面证明简化剩余系的一些推论。 > 推论 $1$:该剩余系对于 $\bmod \ m$ 的乘法具有封闭性。 证明:$\forall r_i, r_j \in \Phi_m$,都有 $r_i \bot m, r_j \bot m$。因此有 $r_i r_j \bot m$。根据性质 $(2), (3)$,$r_i r_j$ 也在 $\Phi_m$ 中。 根据 $a_i \bot m \to a_i \bot a_i ^ {-1}$ 可知,$a_i$ 的乘法逆元也在 $\Phi_m$ 中。因此证明了关于乘法和除法的封闭性,还证明了逆元的存在。 事实上,该简化剩余系 $\Phi_m$ 构成关于模 $m$ 乘法运算的交换群(阿贝尔群)。 > 推论 $2$:$\varphi(m) = |\Phi_m|$。 > 推论 $3$:对于 $m > 2$,有: > $$\sum _{r \in \Phi_m} r = 0$$ 证明:可能算是感性证明。根据欧几里得算法的流程,$(r, m) = 1$,则 $(m, m - r) = 1$,也即 $(m, -r) = 1$。因此,如果 $r$ 在 $\Phi_m$ 中,则 $-r$ 也在 $\Phi_m$ 中。由于 $r$ 与 $-r$ 模 $m$ 不相等,因此简化剩余系中的数总是成对出现。相加可以证明。 这也说明,简化剩余系 $\Phi_m$ 的大小 $|\Phi_m|$ 一定是偶数($m > 2$)。 > 推论 $4$:若 $a \bot m$ 且 $\mathbb{\Phi_m}$ 是一个完全剩余系,则 $S = \{ra | r \in \Phi_m\}$ 构成一个完全剩余系。 证明方法可以参考完全剩余系性质 $2$ 的证明。
欧拉定理
定义:若
证明:对于
根据
由此可以得到更弱的定理 Farmet 小定理:
扩展欧拉定理
扩展欧拉定理的证明就不在我能力所及的范围内了。在这就放个结论吧。
这个柿子就比较有用了,可以用来搞降幂之类的事情。
欧拉降幂模板代码
例题
- P4139 上帝与集合的正确用法
求
设
int solve(int p) {
if (p == 1) return 0;
int phi = get_phi(p);
int s = solve(phi);
return qpow(2, s + phi, p);
}
- CF906D Power Tower
给定一个数列
考虑欧拉降幂。欧拉函数下降速度很快,
上面讨论的都是幂次大于
如果数列的每一项都
下面是一份可供参考的代码实现:
#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;
}