『笔记』数学数论(八)
写在前面
\(2021.5.10 \quad update\) 添加了扩展欧拉定理、部分例题、欧拉群论的证明链接,优化代码模板。
简介
对于任意实数 \(n\) ,若满足 \(\gcd(a,n)=1\),则有
欧拉函数
定义
欧拉函数 \(\varphi(n)\) 表示在 \(1 \sim n\) 中,于 \(n\) 互质的数的数量。
已知上述 \(a^{\varphi(n)} \equiv 1 \pmod n\),若等式两边同除于 \(a\) ,则有 \(a^{\varphi(n)-1} \equiv \cfrac{1}{a} \pmod n\).
也就是说,在 \(\pmod n\) 意义下,除以 \(a\) 就相当于乘以 \(a^{\varphi(n)-1}\),而此时的 \(\varphi(n)\) 被称为欧拉函数。
例如:\(\varphi(6) = 2\),\(\varphi(30)=8\)
容斥原理求欧拉函数
容斥原理
应用
已知一个整数 \(n\),它必然可以分解为 \(p_1^{r_1}p_2^{r_2}p_3^{r_3}\cdots p_k^{r_k}\),其中 \(p\) 为 \(n\) 的质因子。
那么对于 \(n\),其中 \(p_1\) 及其倍数的因子数量即为 \(\cfrac{n}{p_1}\),\(p_2\) 及其倍数的因子数量即为 \(\cfrac{n}{p_2}\),\(\cdots\)。
显然,\(1 \sim n\) 中与 \(n\) 不互质的因子的数量即为 \(\cfrac{n}{p_1}+\cfrac{n}{p_2}+\cfrac{n}{p_3}+\cdots+\cfrac{n}{p_k}\)。相反,与 \(n\) 互质的因子的数量即 \(\varphi(n)\) 为 \(n - (\cfrac{n}{p_1} + \cfrac{n}{p_2} + \cfrac{n}{p_3} + \cdots + \cfrac{n}{p_k}) + NUM - \cfrac{n}{p_{1}p_{2}p_{3}\cdots p_{k}}\) 。
其中 \(NUM\) 为同时是多个因子的倍数的数的数量。
不难得出,
原式可以因式分解
代码
int phi(int n) //求 varphi(n)
{
int res = n;
for (int i = 2; i <= sqrt(n); i++)
if (!(n % i))
{
res = res / i * (i - 1);
while (!(n % i))
n = n / i;
}
if (n > 1)
res = res / n * (n - 1);
return res;
}
注意
当 \(\gcd(a,n) \neq 1\) 时,\(a\) 在 \(\pmod n\) 意义下不存在逆元。
也就是说,当 \(a,n\) 不满足 \(a \perp n\) 时,\(a,n\) 无法在 \(\pmod n\) 意义下作除法。
欧拉函数表
大多数情况下,欧拉函数是可以直接拿来用的,所以如果遇到此类题目,需要首先预处理欧拉函数表。
const int _ = 1000010;
int varphi[_], prime[_], num, fac[_];
void Phi()
{
varphi[1] = 1;
for (int i = 2; i <= n - 1; i++)
{
if (!fac[i])
{
prime[++num] = i;
fac[i] = i;
varphi[i] = i - 1;
}
for (int j = 1; j <= num; j++)
if (prime[j] * i > n || prime[j] > fac[i])
break;
else
{
fac[prime[j] * i] = prime[j];
if (prime[j] < fac[i])
varphi[prime[j] * i] = prime[j] * varphi[i] - varphi[i];
else
varphi[prime[j] * i] = prime[j] * varphi[i];
}
varphi[i] += varphi[i - 1];
}
}
附:逆元扩展
已知一个质数 \(p\) ,一个实数 \(n\),且 \(n<p\) ,求 \(1 \sim n\) 在 \(\pmod p\) 意义下的逆元。
首先求出 \(n!\) 的逆元,则 \(n\) 的逆元为 \((n-1)!\cdot (n!)^{-1}\),即 \(\cfrac{(n-1)!}{n!}=\cfrac{1}{n}\)。
依此类推,将 \((n!)^{-1}\) 乘以 \(n\),可得到 \([(n-1)!]^{-1}\) \(\cdots\)
直到推到 \(1\) 。
时间复杂度 \(O(n)\) 。
YES!
例题
P2158 [SDOI2008]仪仗队
对于此题,非常显然每个点与原点生成的矩形对角线上的所有点,都只能看到第一个。也就是说,对于任意整点 \((x,y)\) ,点 \((\lambda x,\lambda y)\) ,\((\lambda >1)\) 都是看不到的。
那么有
即对于一个点 \((x,y)\) ,若其满足 \(\gcd(x,y) \neq 1\) 时,这个点必定看不到。
相反,若其满足 \(\gcd(x,y)=1\) ,且它会被遮挡,则一定存在一个 \(\mu(\mu >1)\) 使得 \((\cfrac{x}{\mu},\cfrac{y}{\mu})\) 能被看到。
又因为 \(\gcd(x,y)=1\),所以 \(\mu\) 一定不存在。
那么可以得出一个结论:一个点 \((x,y)\) 能被看到,当且仅当 \(\gcd(x,y)=1\) 。
当 \(n=1\) 时,答案为 \(0\)。
否则,答案为 \(\sum \limits_{x=0}^{n-1} \sum \limits_{y=0}^{n-1}[\gcd(x, y)=1], n \geq 2\)。
展开
其中 \(n \geq 2\)。
根据
有
其中 \(n \geq 2\) 。
引入欧拉函数 \(\varphi(n)\)
有
其中 \(n \geq 2\)。
综上所述,
/*
Name: P2158 [SDOI2008]仪仗队
Solution: 欧拉函数
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define int long long
#define InF 0x3f3f3f3f
#define kMax 10e5
#define kMin -10e5
#define kMod 998244353
using namespace std;
/*==================================================快读*/
inline int read()
{
int X = 0, F = 1;
char CH = getchar();
while (CH < '0' || CH > '9')
{
if (CH == '-')
F = -1;
CH = getchar();
}
while (CH >= '0' && CH <= '9')
{
X = (X << 3) + (X << 1) + (CH ^ 48);
CH = getchar();
}
return X * F;
}
/*===============================================定义变量*/
int n;
const int _ = 1000010;
int phi[_], prime[_], num, fac[_];
/*=============================================自定义函数*/
void Phi()
{
phi[1] = 1;
for (int i = 2; i <= n - 1; i++)
{
if (!fac[i])
{
prime[++num] = i;
fac[i] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= num; j++)
if (prime[j] * i > n || prime[j] > fac[i])
break;
else
{
fac[prime[j] * i] = prime[j];
if (prime[j] < fac[i])
phi[prime[j] * i] = prime[j] * phi[i] - phi[i];
else
phi[prime[j] * i] = prime[j] * phi[i];
}
phi[i] += phi[i - 1];
}
}
/*=================================================主函数*/
signed main()
{
n = read();
Phi();
printf("%lld\n", n == 1 ? 0 : 2 * phi[n - 1] + 1);
return 0;
}
扩展欧拉定理
我们已知,只有当 \(a,n\) 满足 \(\gcd(a,n)=1\) 时,欧拉定理才成立,那么若 \(a,b\) 不互质呢?
定义
对于 \(a,n\) 若 \(\gcd(a,n) \neq 1\) ,时,有
证明
不妨首先考虑一个质因子 \(p\),令 \(n=s \cdot p^r ,\gcd(s,p)=1\) 。
由 \(p^{\varphi(s)} \equiv 1 \pmod s\) , 又由 \(\gcd(s,p)=1\) ,则有 \(\varphi(s) \mid \varphi(n)\),即 \(p^{\varphi(n)} \equiv 1 \pmod s\)。
根据同余式性质,两边同时乘以一个 \(p^r\)
那么有
其中 \(c \geq r\)
显然, \(r \leq \varphi(n)\)
因此
其中 \(c \geq \varphi(n)\)
上式可转化为
其中 \(c \geq \varphi(n)\) 。
类似的,可以有
其中 \(c \geq \varphi(n),k>0\) 。
由于 \(p\) 是 \(a\) 的因子,即 \(a = \prod p_{i}^{k_{i}}\) ,而对于每一个 \(p\) 都满足上述证明,那么其乘积也必然满足,所以有
其中 \(c \geq \varphi(n)\),
证毕。
例题
P5091 【模板】扩展欧拉定理
RT,模板题。
/*
Name: P5091 【模板】扩展欧拉定理
Solution: 扩展欧拉定理
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define int long long
#define InF 0x3f3f3f3f
#define kMax 10e5
#define kMin -10e5
#define kMod 998244353
using namespace std;
/*==================================================快读*/
inline int read()
{
int X = 0, F = 1;
char CH = getchar();
while (CH < '0' || CH > '9')
{
if (CH == '-')
F = -1;
CH = getchar();
}
while (CH >= '0' && CH <= '9')
{
X = (X << 3) + (X << 1) + (CH ^ 48);
CH = getchar();
}
return X * F;
}
/*===============================================定义变量*/
int a, m, b;
/*=============================================自定义函数*/
inline int read_(int kmod)
{
int X = 0;
bool F = false;
char CH = getchar();
while (CH < '0' || CH > '9')
CH = getchar();
while (CH >= '0' && CH <= '9')
{
X = (X << 3) + (X << 1) + (CH ^ 48);
if (X >= kmod)
F = true;
X %= kmod;
CH = getchar();
}
return X + (F ? kmod : 0);
}
int phi(int n) //求 varphi(n)
{
int res = n;
for (int i = 2; i <= sqrt(n); i++)
if (!(n % i))
{
res = res / i * (i - 1);
while (!(n % i))
n = n / i;
}
if (n > 1)
res = res / n * (n - 1);
return res;
}
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int Qpow(int a, int t, int p)
{
int res = 1;
while (t)
{
if (t & 1)
res = res * a % p;
a = a * a % p;
t >>= 1;
}
return res;
}
/*=================================================主函数*/
signed main()
{
a = read();
m = read();
int t = phi(m);
b = read_(t);
int g = gcd(a, m);
// if (g = 1)
// printf("%lld\n", Qpow(a, b % t, m));
// else if (g != 1 && b < t)
printf("%lld\n", Qpow(a, b, m));
// else if (g != 1 && b >= t)
// printf("%lld\n", Qpow(a, b % t + t, m));
return 0;
}
欧拉定理的群论证明
详情可参见这篇博客。