欧拉定理 & 扩展欧拉定理 笔记

欧拉函数

欧拉函数定义为:φ(n) 表示 1n 中所有与 n 互质的数的个数。

关于欧拉函数有下面的性质和用途:

  • 欧拉函数是积性函数。可以通过这个性质求出他的公式。

    • f(p)=p1。很显然,比质数 p 小的所有数都与他互质。

    • f(p2)=p×(p1)。显然,对于这 p2 个数,只有 p,2p,3pp2 不与 p2 互质。

    • f(pk)=pk1(p1)

    • 假设 n=kpici,则 f(n)=kf(pici)=kpkck1(pk1)=nkpk1pk

  • 欧拉函数可以用线性筛筛出来。

    假设当前的数是 n,遍历到的质数为 p,那么 m=p×n 肯定要被筛掉。根据欧拉筛:

    • p|n,那么 pn 的最小质因子,且 n 中包含 m 的所有质因子。根据单个欧拉函数的求法可以得到:φ(m)=φ(n)×p

    • 否则,pn 互质,根据积性函数的性质,φ(m)=φ(n)φ(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]];
        }
    }
}
  • 欧拉函数可以用来降幂。下面就来介绍。

完全剩余系

对于整数集 S={r1,r2rn} 满足:

  • 任意不同元素 ri,rj,都有 rirj(mod m)

  • aZrS,ra(mod m)

例如,对于 m=5S={0,1,2,3,4} 就是 5 的一个完全剩余系。通常地,将模 m 的完全剩余系记做 Zm

实际应用中,通常用 Zm={0,1,2m1} 来表示,也就是模 m 的最小非负完全剩余系。

推论 1:任意 m 个连续整数构成模 m 的完全剩余系。

推论 2:若 amZm 是一个完全剩余系,则 S={a,2ama} 构成一个完全剩余系。

下面证明推论 2

设集合 S={ra|rZm}。对于 ri,rjZm,rirj。假设存在同余 riarja(mod m)。由于 am,因此 rirj(mod m)。根据完全剩余系的互异性,假设不成立。S 的互异性证明完成。

因为 SZ,由定义 (2) 可得,aSZm 中有唯一的 r 满足 ra(mod m)。因此构成 SZm 的单射。又因为 |S|=|Zm|,故构成 SZm 的双射。

则对于任意整数,都存在 Zm 中的某个数 r 与之同余,亦存在 S 中某个数与之同余。定义 (1) 证明完成。因此 S 是一个完全剩余系。

简化剩余系

在完全剩余系基础上加上了更强的限制。

定义 Φm={r1,r2rs} 为模 m 的简化剩余系,当且仅当:

  • 任意不同元素 ri,rjΦm,都有 rirj(mod m)

  • aZrΦm,ra(mod m)

  • rΦm,rm

其中定义 (2),(3) 可以合并为 am,都存在唯一的 rΦm 满足 ar(mod m)

下面证明简化剩余系的一些推论。

推论 1:该剩余系对于 mod m 的乘法具有封闭性。

证明:ri,rjΦm,都有 rim,rjm。因此有 rirjm。根据性质 (2),(3)rirj 也在 Φm 中。

根据 aimaiai1 可知,ai 的乘法逆元也在 Φm 中。因此证明了关于乘法和除法的封闭性,还证明了逆元的存在。

事实上,该简化剩余系 Φm 构成关于模 m 乘法运算的交换群(阿贝尔群)。

推论 2φ(m)=|Φm|

推论 3:对于 m>2,有:

rΦmr=0

证明:可能算是感性证明。根据欧几里得算法的流程,(r,m)=1,则 (m,mr)=1,也即 (m,r)=1。因此,如果 rΦm 中,则 r 也在 Φm 中。由于 rrm 不相等,因此简化剩余系中的数总是成对出现。相加可以证明。

这也说明,简化剩余系 Φm 的大小 |Φm| 一定是偶数(m>2)。

推论 4:若 amΦm 是一个完全剩余系,则 S={ra|rΦm} 构成一个完全剩余系。

证明方法可以参考完全剩余系性质 2 的证明。

欧拉定理

定义:若 n,a 均为正整数,且 na 互质,则有 aφ(n)1(mod n)

证明:对于 n 的简化剩余系 Φn,根据简化剩余系的推论 4 可知,S={ar|rΦn} 也构成模 n 的简化剩余系。

根据 rΦnrrSr(mod n),有 a|Φn|=aφ(n)1(mod n)。证毕。

由此可以得到更弱的定理 Farmet 小定理:pPap11(mod p)

扩展欧拉定理

扩展欧拉定理的证明就不在我能力所及的范围内了。在这就放个结论吧。

{abab(mod p), b<φ(p)ababmodφ(p)+φ(p)(mod p), bφ(p)

这个柿子就比较有用了,可以用来搞降幂之类的事情。

欧拉降幂模板代码

例题

  1. P4139 上帝与集合的正确用法

2222modp 的值。

f(p) 表示 2 的幂塔对 p 取模的值。那么他就等于 2f(φ(p))+φ(p)。由于 φ 函数下降速度极快(log 速度),很快 φ(p) 降为 1。这个东西可以递归求解。

int solve(int p) {
	if (p == 1) return 0;
	int phi = get_phi(p);
	int s = solve(phi);
	return qpow(2, s + phi, p);
}
  1. CF906D Power Tower

给定一个数列 w1,w2wn 和模数 p, 每次询问一个区间 [l,r],求 wlwl+1wl+2wrmod p 的值

考虑欧拉降幂。欧拉函数下降速度很快,O(logp) 次就能降为 1。因此可能只需要计算到 wl+k,后面的就全模 1 了(当然模 1 就是 0)。这个 kO(logp) 级别的。

上面讨论的都是幂次大于 φ(p) 的情况。如果幂次小于 φ(p) 呢?怎么提前判断掉呢?

如果数列的每一项都 2,那么可以直接暴力判断,因为 O(logp) 个数乘起来就会达到 p。但是如果存在 1 呢?这个也好办,直接把 r 调到 l 后边的第一个 1 前面就行,这样保证了 [l,r] 中没有 1。同时,这个 1 对答案也没有影响,因为 x1=x,1x=1

下面是一份可供参考的代码实现:

#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;
}
posted @   Link-Cut-Y  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示