数论总结
数学是毒瘤
基础数论总结。
数论题的代码都是一个个板子拼起来的,本博客只放板子。
声明:本博客中出现的所有代码,都视为加入了 #define int long long
数论题的特点
-
题目大意简洁易懂。
但有的题还是会古舟一堆 -
码量小,全是板子
-
极其难想,需要手推公式
-
long long 是标配
筛法
筛法可以储存所有的质数,也可以
埃氏筛法
时间复杂度
vector<int> pri;
bool is_prime[MAXN];
void primes(int n) {
for (int i = 2; i <= n; i++) {
if (is_prime[i]) continue;
pri.push_back(i);
for (int j = i; j <= n / i; j++) is_prime[i * j] = 1;
}
}
线性筛法
线性筛法,也称欧拉筛法,时间复杂度
vector<int> pri;
bool is_prime[MAXN];
void primes(int n) {
for (auto &x : is_prime) x = 1;
for (int i = 2; i <= n; i++) {
if (is_prime[i]) pri.push_back(i);
for (auto j : pri) {
if (i * j > n) break;
is_prime[i * j] = 0;
if (i % j == 0) break;
}
}
}
分解质因数
任何一个大于
其中
算术基本定理的推论
试除法分解质因数
int m, p[MAXN], c[MAXN];
void divide(int n) {
m = 0;
for (int i = 2; i * i <= n; i++)
if (n % i == 0) {
p[++m] = i, c[m] = 0;
while (n % i == 0) {
n /= i;
c[m]++;
}
}
if (n > 1) p[++m] = n, c[m] = 1;
}
最大公约数
欧几里得算法
即辗转相除法,时间复杂度
inline int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
欧拉函数
定义
定义
例如
当
2024/7/11 upd. 欧拉函数的公式如下:(其中
为 的所有质因数)
单个数求欧拉函数
inline int euler_phi(int x) {
int res = x;
for (int i = 2; i * i <= x; i++)
if (x % i == 0) {
res = res / i * (i - 1);
while (x % i == 0) x /= i;
}
if (x > 1) res = res / x * (x - 1);
return res;
}
筛法求欧拉函数
利用线性筛法可以快速求出多个数的欧拉函数值。
constexpr int MAXN = 5e6 + 5;
vector<int> prime;
int phi[MAXN];
void euler(int n) {
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!phi[i]) {
prime.push_back(i);
phi[i] = i - 1;
}
for (auto j : prime) {
if (i * j > n) break;
if (i % j != 0)
phi[i * j] = phi[i] * phi[j];
else {
phi[i * j] = phi[i] * j;
break;
}
}
}
}
扩展欧拉定理
欧拉定理
若正整数
欧拉定理的推论
若正整数
当
扩展欧拉定理可用于解决大整数乘方取模问题。
扩展欧几里得
裴蜀定理
对于任意整数
扩展欧几里得算法可以在求得
由于形如
扩展欧几里得算法同样可以求单个数的乘法逆元。
int exgcd(int a, int b, int &x, int &y) {
if (!b) return x = 1, y = 0, a;
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
乘法逆元
费马小定理
若
另一个形式:对于任意整数
定义
如果一个线性同余方程 其实就是倒数
若
如果只是保证
扩展欧几里得求单个数的乘法逆元
参见上文,不再赘述。
费马小定理求逆元
参见上文,代码如下:
inv[0] = 1;
for (int i = 1; i < MAXN; i++) inv[i] = power(i, MOD - 2, MOD);
时间复杂度
线性求逆元
求
#include <bits/stdc++.h>
#define int long long
using namespace std;
constexpr int MAXN = 3e6 + 5;
int n, p, inv[MAXN];
signed main() {
cin >> n >> p;
inv[1] = 1;
for (int i = 2; i <= n; i++) inv[i] = (p - p / i) * inv[p % i] % p;
for (int i = 1; i <= n; i++) cout << inv[i] << '\n';
return 0;
}
中国剩余定理
引入
「物不知数」问题:有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?
定义
中国剩余定理 (Chinese Remainder Theorem, CRT) 可求解如下形式的一元线性同余方程组(其中
上面的「物不知数」问题就是一元线性同余方程组的一个实例。
过程
- 计算所有模数的积
; - 对于第
个方程:- 计算
; - 计算
在模 意义下的逆元 ; - 计算
(不要对 取模)。
- 计算
- 方程组在模
意义下的唯一解为: 。
实现
int crt(int k, int r[], int a[]) {
int n = 1, ans = 0;
for (int i = 1; i <= k; i++) n *= r[i];
for (int i = 1; i <= k; i++) {
int m = n / r[i], b, y;
exgcd(m, r[i], b, y);
ans = (ans + a[i] * m * b % n) % n;
}
return (ans % n + n) % n;
}
扩展中国剩余定理(exCRT)
先考虑只有两个方程的情况,设两个方程分别是
将它们转化为不定方程:
由裴蜀定理,当
则原来的两方程组成的模方程组的解为
那么多个方程呢?只需用上面的方法两两合并即可。
int excrt() {
int x, y;
int M = bi[1], ans = ai[1];
for (int i = 2; i <= n; i++) {
int a = M, b = bi[i], c = (ai[i] - ans % b + b) % b;
int gcd = exgcd(a, b, x, y), bg = b / gcd;
if (c % gcd != 0) return -1;
x = mul(x, c / gcd, bg);
ans += x * M;
M *= bg;
ans = (ans % M + M) % M;
}
return (ans % M + M) % M;
}
注意里面需要用到龟速乘。不过我直接 __int128。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】