0.概述
用来解决幂取模的问题。
1.定理
1.1.基础版
当
a,p 互质时,我们有
aφ(p)≡1(modp)
理由是,称所有与
p 互质的数组成的集合为
S(即
S={x∈[1,p]∣∣gcd(x,p)=1}),则
{ramodp∣∣r∈S}=S
因为
gcd(ra,p)=1 且两两不同。若
r1a≡r2a 则
r1≡r2(modp) ,而这是荒谬的。
于是
r∈S∏(ra)≡r∈S∏r(modp)
左式显然等于
aφ(p)∏r∈Sr ,因为
S 的大小是
φ(p) 。于是乎
aφ(p)r∈S∏r≡r∈S∏r(modp)
而
∏r∈Sr 与
p 互质,可以消去。剩下的就是
aφ(p)≡1(modp)
1.2.升级版
对于任意
a,p,x(φ(p)≤x) 满足
ax≡axmodφ(p)⋅aφ(p)(modp)
理由是精妙的。首先说明这一点:对于任意质数
p 和正整数
q 满足
φ(pq)≥q 。构造法即可:此
q 个数为
pi−1(
i 取
1,2,3,…,q 就恰好有
q 个)。然后小分类讨论:
- 互质的情况。
当
gcd(a,p)=1 时,就是 1.1.基础版
的情形,故成立。
- 不互质的情况。
当
p 只有一个质因子时,设
p=qk ,因为
gcd(a,p)=1 ,所以
a 也同样含有此质因子。而
x≥φ(p)=φ(qk)≥k ,所以
qk∣ax 。所以
ax≡0(modp)
这是对
∀x≥φ(p) 都成立的。而
xmodφ(p)+φ(p) 显然也是一个合法的
x 取值。于是
ax≡axmodφ(p)⋅aφ(p)(modp)
其实两边都是零,蠢透了。
当
p 不只有一个质因子时,设
p=q1k1q2k2⋯qnkn 为其质因数分解,记
pi=qiki 。由于
x≥φ(p)≥φ(pi) ,肯定有
ax≡axmodφ(pi)⋅aφ(pi)(modpi)
而这个式子的本质是什么?是
aφ(pi)≡a2φ(pi) 。没看出来充分,至少看出来其必要。
两边都变成原来的
∏j=iφ(pj) 次方,取模意义下等式仍成立。故
aφ(p)≡a2φ(p)(modpi)
因为
∏j=1nφ(pj)=φ(p) ,积性函数嘛。
注意到
x,y 如果在模
a 和模
b 意义下都相等,那二者在模
lcm(a,b) 意义下亦相等。于是得到
aφ(p)≡a2φ(p)(modp)
而上式是充分的,自行验证不难。结论即是开头的式子。
2.例题
2.1.板题
2.1.1.题目
Calculation:定义
f(n)=(nmod10)f(⌊10n⌋) ,边界为
f(0)=1 ,求
f(n)modm 。
n,m≤109 。
2.1.2.思路
显然唯一问题就是
f 的值很大。利用欧拉降幂即可。怎么判断
f(n) 的指数是否超过了
φ(m) 呢?
注意到
aφ(p)≡a2φ(p) 这个式子中,两边的值明显不同(除非
a≤1 ,因为
φ(p)=0 恒不成立)。如果要相等,则右式一定超过了模数。故
a2φ(p)≥p
注意
aφ(p)≥p 未必成立。比如
φ(6)=2 就会被
a=2 绝杀。
于是用
f(n,m) 表示
f(n) (题面中的那个)取模
m 的值。
g(n,m) 表示
f(n) 是否不小于
m 。有递推式
f(n,m)=(nmod10)f[⌊10n⌋,φ(m)]+g[⌊10n⌋,φ(m)]⋅φ(m)modm
g(n,m)=f(n,m)∨g[⌊10n⌋,2φ(m)]
第二个式子中的
f(n,m) 大家都懂的,就是计算过程中超过了
m 就行。但是两个式子的递归情况不同,比较烦。因为我们必须保证
g(n,m) 中涵盖了两种情况——递归中取模了;未取模,幂时爆了。
但是我们有妙招:在
g(n,2m)=1 时,
f(n,m)+g(n,m)⋅m=f(n,2m)modm+m ;在
g(n,2m)=0 时,
f(n,m)+g(n,m)⋅m=f(n,2m) 。
所以
f(n,m) 的方程式中的指数可以被完全表示出来了,只递归
2φ(m) 的情况即可。
2.1.3.代码
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
int_ getphi(int_ n){
int_ res = n;
for(int i=2; i<=n/i; ++i)
if(n%i == 0){
res = res/i*(i-1);
while(!(n%i)) n /= i;
}
if(n > 1) res = res/n*(n-1);
return res;
}
int_ mul(int_ a,int_ b,int_ c){
int_ ans = 0; if(a < b) swap(a,b);
for(; b; b>>=1,a=(a<<1)%c)
if(b&1) ans = (ans+a)%c;
return ans;
}
bool over;
int_ qkpow(int_ bas,int_ q,int_ m){
int_ ans = 1; bool zxy = 0;
for(; q; q>>=1){
if(q&1){
if(bas && ans > (m-1)/bas)
over = true;
if(zxy) over = true;
ans = mul(ans,bas,m);
}
if(bas && bas > (m-1)/bas)
zxy = true;
bas = mul(bas,bas,m);
}
return ans;
}
int_ f(int n,int_ m){
if(n == 0){
over = 0;
return 1;
}
if(n%10 == 1){
over = 0; return 1;
}
if(!(n%10)){
if(f(n/10,m) || over){
over = 0; return 0;
}
return 1;
}
int_ phi = getphi(m);
int_ x = f(n/10,phi<<1);
if(over) x = x%phi+phi;
return qkpow(n%10,x,m);
}
int main(){
int T, n, m;
for(scanf("%d",&T); T; --T){
scanf("%d %d",&n,&m);
printf("%lld\n",f(n,m));
}
return 0;
}
2.1.4.后记
搜到的题解中还有另一种做法,就是认为
aφ(p)≥p ,故而直接求
f(n,m)+g(n,m)⋅m 的值,
g 只在计算幂时考虑,不递推。
为什么这是对的呢?因为 我太敏感一来就找到反例 只有
2φ(6)<6 。只有
6 能做到!
如果要构造反例,则需要
φ(p)=6 ,故
p∈{7,9,14,18} 。但这几个模数都无法构造出反例。
所以究竟为什么只有 6 满足这个式子啊,淦。
有些题解是完全错误的,只需要试试
f(253)mod36 就能找出问题。答案应当为
27 。
这道题中的
00=1 的规定也非常不友好。我因为这一条
WA 了十几发。