∀a<m,(a,m)=1,均有aφ(m)≡1(modm),于是就有欧拉定理:
an≡anmodφ(m)(modm)
对于n取值较大的情况(比如1030000mod13),欧拉降幂的作用不可忽视,但局限的是必须满足(a,m)=1才能有上式恒成立。
由于上述形式流传甚广,读者大抵早已熟记于心,这里便不再赘述。本文的最终目的是介绍另一个适用范围更广的版本,即所谓拓展欧拉定理。
an≡anmodφ(m)+φ(m)(modm)if n≥φ(m)
证明 - 欧拉定理#
证明方法来自《初等数论》柯召
考虑这样一组数r1,r2,…,rφ(m),满足∀ri∈{r1,r2,…,rφ(m)},s.t.(ri,m)=1,同时∀i=j,(ri,rj)=1。
当(a,m)=1,我们可以得到:
(ar1)(ar2)…(arφ(m))(r1r2…rφ(m))×(aφ(m)−1)≡≡r1r2…rφ(m)(modm)0(modm)
由于(ri,m)=1,可知(r1r2…rφ(m),m)=1,故aφ(m)≡1(modm)。
拓展情况#
这里的讨论是基于(a,m)=1的情况来的,因为当(a,m)=1成立时直接用欧拉定理就能推知上式成立。
由于n=⌊φ(m)n⌋×φ(m)+⟨n⟩φ(m)≥φ(m),不妨令n=λφ(m)+⟨n⟩φ(m),λ∈Z+。
问题转换为证明aλφ(m)≡aφ(m)(modm),这可以从λ=2的情况递推过去,下面证明λ=2时同余式成立。
将a因式分解,其中与m互素的部分可以直接使用欧拉定理消去,剩余的素数幂乘积分开后的情况和原问题同一结构,所以只考虑单个素数情况即可。原问题等价为证明:p2×φ(m)≡pφ(m)(modm),p∈Prime。
当p∤m时显然成立。
否则,令ps∣∣m,m=k×ps,显然有pφ(m)≡1(modk),不妨令pφ(m)=kq+1,q∈Z。
- 问题进一步转换为证明(kq+1)2≡(kq+1)(modm),即证明kps∣kq×(kq+1)
- 代入kq+1=pφ(m),即证明ps∣q×pφ(m)
- 由于φ(m)≥φ(ps),只需要证明φ(ps)=ps−1(p−1)≥s
下面是证明:
由Bernoulli’s Inequality对左式放缩:ps−1(p−1)≥(p−1)×[1+(s−1)×(p−1)]
即证:(p−1)×[1+(s−1)×(p−1)]−s≥0,
LHS=s(p−2)p−(p2−3p+2)≥s(p−2)p−(p2−3p+p)=(s−1)(p−2)p≥0=RHS
(高考之后就很少碰不等式证明了,手生了2333。其实有更短的证明:φ(ps)≥2s>s,上面的证明纯粹是写着好玩。)
应用示例#
链接:https://ac.nowcoder.com/acm/problem/17190 来源:牛客网
题目描述
给一个长为n的序列,m次操作,每次操作:
区间[l,r]加x
对于区间[l,r],查询a[l]a[l+1]a[l+2]…modp,一直到a[r]
请注意每次的模数不同。
数据范围
n,m<=500000
序列中每个数在 [1,2e9] 内,x<=2e9,p<=2e7
容易知道,每次考虑取模φ(m),接近logm次就能到1,也就是说用拓展欧拉定理直接暴力递归即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
| #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e7 + 10;
bitset<maxn> ok;
int pr[maxn], cnt;
int phi[maxn];
void init(int n) {
phi[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!ok[i]) phi[pr[++ cnt] = i] = i - 1;
for (int j = 1; j <= cnt && pr[j] <= n / i; ++j) {
ok[pr[j] * i] = 1;
if (i % pr[j] == 0) {
phi[pr[j] * i] = phi[i] * pr[j];
break;
} phi[pr[j] * i] = phi[i] * (pr[j] - 1);
}
} return ;
}
int n, m, x;
ll d[maxn];
inline void add(int p, ll v) {
for (; p <= n; p += p & -p) d[p] += v;
}
inline ll query(int p, ll res = 0LL) {
for (; p; p &= p - 1) res += d[p];
return res;
}
inline ll qpow(ll x, ll n, ll mod, ll res = 1, ll tag = 0) {
if (x >= mod) x %= mod, tag = 1;
while (n) {
if (n & 1) {
res = res * x;
if (res > mod) res = res % mod, tag = 1;
} n >>= 1;
x = x * x;
if (x > mod) x = x % mod, tag = 1;
} return res + mod * tag;
}
inline ll slove(int l, int r, ll p) {
ll v = query(l);
if (l == r || p == 1LL) return (v > p) ? (v % p + p) : (v);
return qpow(v, slove(l + 1, r, (ll)phi[p]), (ll)p);
}
int main() {
init(2e7);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%d", &x), add(i, x), add(i + 1, -x);
while (m--) {
int op, l, r;
scanf("%d%d%d%d", &op, &l, &r, &x);
if (op == 1) {
add(l, x), add(r + 1, -x);
} else {
printf("%lld\n", slove(l, r, x) % x);
}
}
return 0;
}
|