基础数论
1 同余
1.1 定义
若
1.2 性质
同余有以下性质:
- 自反性:
。 - 对称性:若
,则 。 - 传递性:若
, ,则 。 - 同加性:若
,则 。 - 同乘性:若
,则 - 同幂性:若
,则
注意同余没有同除性,但是有消去律。
2 素数
2.1 素数的定义
一个大于
2.2 有关素数的定理
2.2.1 算数基本定理
任何一个大于
其中,
2.2.2 与其因子的大小关系
2.2.3 分解质因数
采用试除法,从
2.3 素数判定
仍然考虑试除法,复杂度
2.4 筛素数
2.4.1 埃氏筛
对于求出某个范围内的所有素数,我们从
这样做的时间复杂度为
2.4.2 线性筛(欧拉筛)
为了解决埃氏筛的弊端,每个数现在只会被它的最小质因子筛掉。
流程如下:
从小到大枚举每个数
如果当前数没有被筛掉,则必定是素数,记录。
枚举已记录的素数
1). 如果合数没有越界,划掉合数
2). 如果
,表明 有了一个最小质因子 。根据欧拉筛的定义,到此退出即可。
代码:
int prim[Maxn], tot;
bool vis[Maxn];
void prime() {
for(int i = 2; i <= Maxn; i++) {
if(!vis[i]) prim[++tot] = i;
for(int j = 1; i * prim[j] <= Maxn; j++) {
vis[i * prim[j]] = 1;
if(i % prim[j] == 0) break;
}
}
}
2.5 欧拉函数
2.5.1 定义
-
对于正整数
,他的欧拉函数 指的是小于等于 的数中与 互质的数的个数。 -
通项公式为:
其中,
2.5.2 性质
- 若
为素数,则 - 若
为素数,则 - 欧拉函数是积性函数。
2.5.3 求解欧拉函数
2.5.3.1 试除法
如果只要求一个数的欧拉函数,直接根据定义试除法即可。
int get_phi(int n) {
int ans = n;
int m = sqrt(n);
for(int i = 2; i <= m; i++) {
if(n % i == 0) {
ans = ans * (i - 1) / i;
}
while(n % i == 0) n /= i;
}
if(n > 1) ans = ans * (n - 1) / n;
return ans;
}
2.5.3.2 筛法求欧拉函数
上面算法的时间复杂度为
我们考虑在筛法的过程中求出欧拉函数。
设
若当前
为素数,则 。 若
不为素数: 设
的最小质因子为 ,则 应该被 筛去。 1). 若
能被 整除,则 包含了 所有的质因子。 则
。 2). 若
不被 整除,则 与 互质。 则
。
代码:
bool vis[Maxn];
int prim[Maxn], phi[Maxn], cnt;
void get_phi() {
phi[1] = 1;
for(int i = 2; i <= Maxn; i++) {
if(!vis[i]) {
prim[++cnt] = i;
phi[i] = i - 1;
}
for(int j = 1; i * prim[j] <= Maxn; j++) {
vis[i * prim[j]] = 1;
phi[i * prim[j]] = (prim[j] - 1) * phi[i];
if(i % prim[j] == 0) {
phi[i * prim[j]] = prim[j] * phi[i];
break;
}
}
}
}
2.6 求约数个数和约数和
2.6.1 约数个数
2.6.1.1 公式法
根据算数基本定理,设:
则
由乘法原理易证。
2.6.1.2 筛法求约数个数
设
若
若
设
1). 当
设
则
即
同时,
2). 当
设
则
同时
代码跟筛法求欧拉函数差不多,全部带入公式计算即可。
2.6.2 约数和
2.6.2.1 公式法
根据算数基本定理,设:
则
2.6.2.2 筛法求约数之和
设
若
若
设
1). 当
设
则
即
同时,
2). 当
设
则
同时
代码与筛法求约数个数一样,就不放了。
2.7 欧拉定理与费马小定理
2.7.1 欧拉定理
若
特别的,当
2.7.2 费马小定理
若
2.7.3 扩展欧拉定理
对于
2.7.3.1 秦九昭算法
对于多项式
对于
这样就只需要做
扩展欧拉定理模板:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int Maxn = 2e5 + 5;
int a, p, b;
string s;
int get_phi(int k) {
int ans = k, m = sqrt(k);
for(int i = 2; i <= m; i++) {
if(k % i == 0) {
ans = ans * (i - 1) / i;
}
while(k % i == 0) k /= i;
}
if(k > 1) ans = ans * (k - 1) / k;
return ans;
}
int down_pow(int phi) {
int res = 0;
bool flag;
for(int i = 0; i < s.size(); i++) {
res = res * 10 + s[i] - 48;
if(res >= phi) {
res %= phi;
flag = 1;
}
}
if(flag) res += phi;
return res;
}
int qpow(int a, int b, int p) {
int res = 1;
while(b) {
if(b & 1) {
res *= a;
res %= p;
}
a *= a;
a %= p;
b >>= 1;
}
return res;
}
signed main() {
ios::sync_with_stdio(0);
cin >> a >> p >> s;
int _phi = get_phi(p);
b = down_pow(_phi);
cout << qpow(a, b, p);
return 0;
}
其中函数 down_pow
部分就利用到了秦九昭算法来对
3 最大公约数
3.1 欧几里得算法
欧几里得算法是常用的求
他运用了一个性质:
代码:
void gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
3.2 裴蜀定理
定理说明,设
3.2.1 裴蜀定理的简单推广
设
3.3 扩展欧几里得算法(扩欧)
扩欧算法常用于求方程
推导:
因此,我们可以层层向下递归求解,然后回带到上一层,
代码:
int exgcd(int a, int b, int &x, int &y) {
if(b == 0) {
x = 1;
y = 0;
return a;
}
int ret = exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - a / b * y;
return ret;
}
3.3.1 扩欧后构造通解
通解公式为:
3.3.2 扩欧求解线性同余方程
问题:求方程
将同余方程转化为不定方程:
由
由裴蜀定理知,当
接下来由扩欧求出方程
3.3.2.1 同余方程的消去律
虽然同余没有同除性,但同余方程有消去律。
若
4 逆元
4.1 定义与用途
若
仅当
上面曾经讲到,同余不具有同除性,这是不能忍受的。于是我们就可以用逆元来解决问题。
根据定义推到,得出的结论是:
4.2 求乘法逆元
4.2.1 费马小定理
条件:当
根据费马小定理
所以此时的乘法逆元为
4.2.2 线性求逆元
求
推导:
首先,
接下来,设
在模
两边同乘
设
设
若要求正整数,则递推公式为 :
代码:
inv[1] = 1;
for(int i = 2; i <= n; i++) {
inv[i] = ((p - p / i) * inv[p % i]) % p;
}
4.2.3 欧拉定理求逆元
回顾欧拉定理:当
4.2.4 扩欧求逆元
看
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律