费马小定理
定理
若 (p) 为素数,得:
证明
考虑对于 (p-1) 个数 (a mod p,2a mod p,dots,(p-1)a mod p),其中 (a ot p)。根据 (ac equiv bc pmod{m} Leftrightarrow a equiv b pmod{m}) 得这 (p-1) 个数互不相同,再由抽屉原理得这 (p-1) 个数构成了模 (p) 的一个完全剩余系,即其排序后为 (1,2,dots,p-1)。由此得:
因为 ((p-1)!) 不能被 (p) 整除,所以得 (a^{p-1} equiv 1 pmod{p},a ot p)。
欧拉函数
概念
欧拉函数 (varphi(n)) 表示小于等于 (n) 且和 (n) 互质的数的个数。
性质
-
欧拉函数为积性函数
-
(varphi(p)=p-1) 其中 (p) 为素数。
-
(varphi(p^k)=p^{k-1}(p-1)) 其中 (p) 为素数。将 (p^k) 分成长度为 (p) 的 (p^{k-1}) 段后,得每一段和 (p^k) 互质的数个数为 (p-1),所以总个数为 (p^{k-1}(p-1))。
-
由算术基本定理得,(n=prod p_i^{k_i}) 其中 (p_i) 为素数,得 (varphi(n)=nprodfrac{p_i-1}{p_i}=nprod(1-frac{1}{p_i}))。
[largeegin{aligned} varphi(n) &= prod varphi(p_i^{k_i}) \ &= prod p_i^{k_i-1}(p_i-1) \ &= prod p_i^{k_i}(1-frac{1}{p_i}) \ &= nprod (1-frac{1}{p_i}) \ end{aligned} ] -
(sumlimits_{d mid n} varphi(d) = n),即 (varphi imes 1 = id)。
-
[largesumlimits_{i=1}^ni[i ot n]=frac{n varphi(n)+epsilon(n)}{2} ]
考虑到当 (n>1) 时,因为与 (n) 互质的数总是成对出现,即若 (i) 与 (n) 互质,(n-i) 与 (n) 也互质,每对的和都为 (n),这样的数对个数为 (frac{varphi(n)}{2}),(n=1) 时特殊处理即可。
欧拉定理
定理
证明
和费马小定理的证明类似。
考虑对于 (varphi(m)) 个数 ({ ka mod m mid k ot m , 0 leqslant k < m}),其中 (a ot m),根据抽屉原理得其构成了模 (m) 的一个简化剩余系,即为 ({ k mid k ot m , 0 leqslant k < m }),将这 (varphi(m)) 个数连乘得:
因为 (prodlimits_{k ot m , 0 leqslant k < m} k) 不能被 (m) 整除,所以得 (a^{varphi(m)} equiv 1 pmod{m},aot m)。
扩展欧拉定理
定理
由欧拉定理 (a^{varphi(m)} equiv 1 pmod{m},aot m),第一和第三个式子可以合并:
证明
考虑 (m) 的一个质因子 (p),得 (m=sp^k,s ot p),由欧拉定理得 (p^{varphi(s)} equiv 1 pmod{s})。因为欧拉函数为积性函数,得 (varphi(s) mid varphi(m)),所以 (p^{varphi(m)} equiv 1 pmod{s}),同乘 (p^k) 得 (p^{varphi(m)+k} equiv p^k pmod{m}),得:
因为 (varphi(m) geqslant k),所以 (p^b equiv p^{varphi(m)+b} pmod{m},b geqslant varphi(m)),得:
结合 (b geqslant varphi(m)) 的情况,得:
考虑质因子的幂:
再由算术基本定理 (a=prod p_i^{k_i}) 合并即可,若 (p_i) 不是 (m) 的质因子,就直接应用欧拉定理。
应用
利用扩展欧拉定理,可以处理快速幂取模,即对指数取模,例如【模板】扩展欧拉定理,上帝与集合的正确用法,黑暗打击。
其中 (b) 很大,达到 (10^{20000000})。
ll qp(ll x,ll y)
{
ll v=1;
while(y)
{
if(y&1) v=v*x%m;
x=x*x%m,y>>=1;
}
return v%m;
}
ll get_phi(ll x)
{
ll v=x,t=sqrt(x);
for(int i=2;i<=t;++i)
{
if(x%i) continue;
v=v*(i-1)/i;
while(x%i==0) x/=i;
}
if(x>1) v=v*(x-1)/x;
return v;
}
......
read(a),read(m),phi=get_phi(m);
scanf("%s",s+1),len=strlen(s+1);
for(int i=1;i<=len;++i)
{
b=((b<<1)+(b<<3)+(s[i]^48));
if(b>=phi) b%=phi,flag=true;
}
if(flag) b+=phi;
printf("%lld",qp(a,b));