基础数论
欧拉函数
https://oi-wiki.org/math/number-theory/euler/
欧拉函数是一个积性函数,但不是完全积性函数。
- 积性函数:
,且当 时, 。 - 完全积性函数:
,且 。
性质
欧拉定理
若
证明
设序列
,有 ,也就是 与 互质。这是显然的,因为这是欧拉函数的定义; ,有 。因为所有 都是 的。
现在找到一个数
,有 ,也就是 与 互质。由于 ,又因为 与 互质, 与 互质,所以 也与 互质; ,有 。利用反证法,若 ,则这个式子可以变为 。两边同时 ,也就变成了 ,这与上面的结论不符。故 。.
若将所有的
所以可以得到:
等式两边同时
应用
- 求逆元:
(当 为质数时)。 - 降低指数:
。 - 扩展欧拉定理:
可以不是质数, 可以不与 互质。
求解
- 若
是质数,显然 都与 互质,故 ; - 枚举
,找到 ,分两种情况求解 :- 若
,也就是 是 的最小质因子。那么也就是说在原来的 中就已经有了一个质因子 ,所以 和 的质因子是相同的。假设 的所有质因子是 ,那么 的质因子也是 ,所以有 。其中后面的 就是 的值,所以 ; - 若
,也就是 不是 的最小质因子。那么根据积性函数的定义,显然有 ,也就是 。
- 若
int p[N]; // p[i] 表示第 i 个质数
bool st[N]; // st[i] 表示 i 是否是质数
int phi[N];
void prime(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; p[j] <= n / i; j ++ )
{
st[p[j] * i] = 1;
if (i % p[j]) phi[i * p[j]] = phi[i] * p[j];
else
{
phi[i * p[j]] = phi[i] * (p[j] - 1);
break;
}
}
}
}
除数函数
除数函数全部是积性函数,但不是完全积性函数。
求解
在线性筛约数个数时还需要额外记录一个
- 若
是质数,那么根据质数的定义, 只有两个约数 和 ,故 ,而且显然 ; - 枚举
,找到 ,分两种情况求解 :- 若
,也就是 是 的最小质因子。那么 原来有 这个约数,并且是最小的约数,现在的 相比 又多了一个 ,也就是原来的 变成了 ,也就是 。所以求 时就需要先把原来 的贡献移除,在重新加上 的贡献,也就是 ; - 若
,也就是 不是 的最小质因子。那么也就是说原来的 中没有 这个约数,所以把 分解质因数后应该有一项是 ,剩下的是原来 分解质因数的结果。根据约数个数的公式,将所有的指数加 后连乘,那么就应该在原来 的基础上 ,所以 。
- 若
int p[N]; // p[i] 表示第 i 个质数
bool st[N]; // st[i] 表示 i 是否是质数
int d[N]; // d[i] 表示 σ0(i) 的值
int num[N]; // num[i] 表示 i 的最小质因子出现次数
void prime(int n)
{
d[1] = 1;
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) p[ ++ cnt] = i, d[i] = 2, num[i] = 1;
for (int j = 1; p[j] <= n / i; j ++ )
{
st[p[j] * i] = 1;
if (i % p[j]) num[i * p[j]] = 1, d[i * p[j]] = d[i] * 2; // p[j] 不是 i 的最小质因子
else // p[j] 是 i 的最小质因子
{
num[i * p[j]] = num[i] + 1, d[i * p[j]] = d[i] / (num[i * p[j]] + 1) * (num[i * p[j]] + 2);
break;
}
}
}
}
莫比乌斯函数
https://oi-wiki.org/math/number-theory/mobius/
性质:
莫比乌斯函数是积性函数,但不是完全积性函数。
求解
- 若
是质数,那么 就只有一个质因子 ,所以 ; - 枚举
,找到 ,分两种情况求解 :- 若
,也就是 是 的最小质因子。设 ,那么显然有 ,所以 就有了一个平方因子,所以 ; - 若
,也就是 不是 的最小质因子。那么 就比 多了一个新的质因子 ,故它的本质不同的质因子个数就多了 ,所以 。
- 若
void prime(int n)
{
mu[1] = 1;
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) p[ ++ cnt] = i, mu[i] = -1;
for (int j = 1; p[j] <= n / i; j ++ )
{
st[p[j] * i] = 1;
if (i % p[j]) mu[p[j] * i] = -mu[i]; // p[j] 不是 i 的最小质因子
else // p[j] 是 i 的最小质因子
{
mu[i * p[j]] = 0;
break;
}
}
}
}
欧几里得算法
https://oi-wiki.org/math/number-theory/gcd/#%E6%AC%A7%E5%87%A0%E9%87%8C%E5%BE%97%E7%AE%97%E6%B3%95
求
回溯得到答案。
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
扩展欧几里得算法
求二元一次方程
裴蜀定理
裴蜀定理:有解当且仅当
证明:
-
有解当且仅当
:设
,则 。这个数一定是 的倍数。证毕。 -
是 的最小整数解:设
, 是 的最小整数解,即 。接下来从两个方面证明 。-
:与上面类似。设 ,则 。 -
:因为 且 ,尝试证明 且 。由于 是最大公约数,所以 。反证法:设
,则 ,则 。因为 ,则 ,即 ,即得到一组新的解,使得 ,于 是最小正整数结果矛盾,故 ,同理 。
证毕。
-
求解
把这个方程中的
其中
这样第二个式子就变成了
整理式子:
至此,这个式子与最开始
这是第二层转换到第一层的过程,在这之前如果要求第二个式子需要往下的第三个式子进行推导,那么就需要写递归函数求解了。
当某个
代码:
int exgcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, x, y);
int X = x;
x = y, y = X - a / b * y;
return d;
}
或:
int exgcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1; y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
如果
求逆元
求
也就是求
于是就转化成了二元一次方程,求的是
这个方法对
int inv(int a, int b)
{
int x, y, d = exgcd(a, b, x, y);
if (d == 1) return (x % b + b) % b;
else return -1;
}
筛质数
https://oi-wiki.org/math/number-theory/sieve/
- 埃氏筛:
。
从小到大枚举每一个数字,如果之前被筛掉了,那么不是质数,否则就是质数。
无论当前的
这个筛法从来不用。
- 线性筛:
。
对于每一个数字
在枚举 break
。
int p[N];
bool st[N];
void prime(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) p[ ++ cnt] = i;
for (int j = 1; p[j] <= n / i; j ++ )
{
st[p[j] * i] = 1;
if (i % p[j] == 0) break;
}
}
}
扩展
线筛除了可以筛质数,还可以筛一类满足条件的函数。
可以直接求( 指任意一个质数);- 设
为 的最小质因子, 。
那么已知
中国剩余定理 CRT
https://oi-wiki.org/math/number-theory/crt/
有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?
给定
定义一个
显然
我们可以这样得到一个
时间复杂度
void exgcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1, y = 0;
return;
}
exgcd(b, a % b, y, x);
y -= a / b * x;
}
signed main()
{
cin >> n;
int M = 1;
for (int i = 0; i < n; i ++ )
{
cin >> A[i] >> B[i];
M *= A[i];
}
int res = 0;
for (int i = 0; i < n; i ++ )
{
int Mi = M / A[i], ti, x;
exgcd(Mi, A[i], ti, x);
res += B[i] * Mi * ti;
}
cout << (res % M + M) % M;
return 0;
}
扩展中国剩余定理 EXCRT
如果
求解
如果能把两个式子
如何求
根据裴蜀定理,上式有解当且仅当
扩欧求出
然后不断将方程两两合并即可找到
int exgcd(int a, int b, int &x, int &y) // 扩展欧几里得算法, 求x, y,使得ax + by = gcd(a, b)
{
if (!b)
{
x = 1; y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
int a1, m1; // 现有方程
int a2, m2; // 读入的方程
int k1, k2; // 系数
int d; // 最大公约数
int t;
signed main()
{
cin >> n >> a1 >> m1;
for (int i = 0; i < n - 1; i ++ )
{
cin >> a2 >> m2;
d = exgcd(a1, a2, k1, k2);
if ((m2 - m1) % d) // 裴蜀定理,判断是否无解
{
puts("-1");
return 0;
}
k1 *= (m2 - m1) / d; // 把 k1a1 - k2a2 = m2 - m1 翻成 k1a1 - k2a2 = d
t = a2 / d;
k1 = (k1 % t + t) % t;
m1 = a1 * k1 + m1;
a1 = abs(a1 / d * a2);
}
cout << (m1 % a1 + a1) % a1;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现