数论总结

数学是毒瘤


基础数论总结。

数论题的代码都是一个个板子拼起来的,本博客只放板子。

声明:本博客中出现的所有代码,都视为加入了 #define int long long

数论题的特点

  1. 题目大意简洁易懂。但有的题还是会古舟一堆

  2. 码量小,全是板子

  3. 极其难想,需要手推公式

  4. long long 是标配


筛法

筛法可以储存所有的质数,也可以 O(1) 判断质数。

埃氏筛法

时间复杂度 O(NloglogN)

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;
	}
}

线性筛法

线性筛法,也称欧拉筛法,时间复杂度 O(N)

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;
		}
	}
}

分解质因数

任何一个大于 1 的正整数 N 都能唯一分解为有限个质数的乘积,可写作:N=p1c1p2c2pmcm

其中 ci 都是正整数,pi 都是质数,且满足 p1<p2<<pm

算术基本定理的推论

N 的正约数个数为:

(c1+1)(c2+1)(cm+1)=i=1m(ci+1)

N 的所有正约数的和为:

(1+p1+p12++p1c1)(1+pm+pm2+pmcm)=i=1m(j=0cipij)

试除法分解质因数

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;
}

最大公约数

欧几里得算法

即辗转相除法,时间复杂度 O(logN)

inline int gcd(int a, int b) {
	if (b == 0) return a;
	return gcd(b, a % b);
}

欧拉函数

定义

定义 φ(n) 表示的是小于等于 nn 互质的数的个数。

例如 φ(1)=1

n 是质数的时候,显然有 φ(n)=n1

2024/7/11 upd. 欧拉函数的公式如下:(其中 p1mx 的所有质因数)

φ(n)=n×i=1m(11pi)

单个数求欧拉函数

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;
			}
		}
	}
}

扩展欧拉定理

欧拉定理

若正整数 a,n 互质,则 aφ(n)1(modn)

欧拉定理的推论

若正整数 a,n 互质,则对于任意正整数 b,有 ababmodφ(n)(modn)

a,n 不一定互质且 b>φ(n) 时,有 aba(bmodφ(n))+φ(n)(modn)

扩展欧拉定理可用于解决大整数乘方取模问题。

扩展欧几里得

裴蜀定理

对于任意整数 a,b,存在一对整数 x,y,满足 ax+by=gcd(a,b)

扩展欧几里得算法可以在求得 gcd(a,b) 的前提下,求解形如 ax+by=gcd(a,b) 的方程的解。

由于形如 axb(modp) 的线性同余方程可以改写为 ax+pk=b 的形式,所以线性同余方程也可用扩展欧几里得求解。当然,由裴蜀定理,有整数解的充要条件为 gcd(a,p)b。线性同余方程也可用逆元求解。计算 a 的逆元,然后两边同除以 a 的逆元可得到一个解。

扩展欧几里得算法同样可以求单个数的乘法逆元。

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;
}

乘法逆元

费马小定理

p 是质数且 a,p 互质,则 ap11(modp)

另一个形式:对于任意整数 a,有 apa(modp)

定义

如果一个线性同余方程 ax1(modb),则 x 称为 amodb 的逆元,记作 a1(modb)其实就是倒数

b 是质数且 a<b,由费马小定理,易得 ab2 即为乘法逆元。

如果只是保证 a,b 互质,则我们可以通过求解同余方程 ax1(modb) 得到乘法逆元。

扩展欧几里得求单个数的乘法逆元

参见上文,不再赘述。

费马小定理求逆元

参见上文,代码如下:

inv[0] = 1;
for (int i = 1; i < MAXN; i++) inv[i] = power(i, MOD - 2, MOD);

时间复杂度 O(NlogN),可以在数据规模不大于 106 时求出逆元。

线性求逆元

P3811 乘法逆元

1n 中所有整数在模 p 意义下的乘法逆元。

1n3×106n<p<20000528,保证 p 为质数。

#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) 可求解如下形式的一元线性同余方程组(其中 n1,n2,,nk 两两互质):

{xa1(modn1)xa2(modn2)xak(modnk)

上面的「物不知数」问题就是一元线性同余方程组的一个实例。

过程

  1. 计算所有模数的积 n
  2. 对于第 i 个方程:
    • 计算 mi=n/ni
    • 计算 mi 在模 ni 意义下的逆元 mi1
    • 计算 ci=mimi1(不要对 ni 取模)
  3. 方程组在模 n 意义下的唯一解为:x=i=1kaici(modn)

实现

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)

n1,n2,,nk 不互质怎么办呢?

先考虑只有两个方程的情况,设两个方程分别是 xa1(modn1)xa2(modn2)

将它们转化为不定方程:x=n1p+a1=n2q+a2,其中 p,qZ,则有 n1pn2q=a2a1

由裴蜀定理,当 a2a1 不能被 gcd(n1,n2) 整除时,无解。否则可以扩欧算出一组解 (p,q)

则原来的两方程组成的模方程组的解为 xb(modM),其中 b=n1p+a1M=lcm(n1,n2)

那么多个方程呢?只需用上面的方法两两合并即可。

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。

posted @   Laoshan_PLUS  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示