数论基础
数论:研究数与数之间的关系。如带余除法、因数倍数、同余整除等都是数论。
【筛法】
主要使用的筛法有两个:埃氏筛和欧拉筛(线性筛)。
使用筛法的原因很简单:对于一个数,我们要找它的因数不容易;但是对于一个因数,我们找它的倍数很简单。
【埃氏筛】
因为一个数的倍数一定不是质数,且任何数都能分解为若干质数之积,所以我们每找到一个质数,就把这个质数的所有倍数都标记为合数。
bool p[N] = {}; //p[i]=true表示i为质数
void shai() {
p[0] = p[1] = false;
for (int i = 2; i <= N; i++)
p[i] = true;
for (int i = 2; i <= N; i++)
if (p[i]) {
for (long long j = i; i * j <= N; j++) //删去所有倍数
p[i * j] = false;
}
}
注意:这里我们还有一个小优化,我们的 j
是从 i
开始枚举的,这是因为在 j < i
时,我们一定在 i
更小的时候就已经标记过 i * j
了。这样可以避免一些重复标记。
但是,我们发现埃氏筛依旧会出现重复标记的情况。例如
因为有重复标记,所以埃氏筛的时间复杂度略高于
【欧拉筛】
我们希望埃氏筛可以进化到
上面埃氏筛慢的原因,就在于一个数被多次标记。而多次标记的原因,是我们分类分的不好。
我们在上面把所有合数分类,这个分类的方法是 “如果
并且,每找到一个质数,我们就把该质数对应的分类中所有的数都打上合数标记。
显然,这样的分类方法可能会让一个数被分入多个类中,从而导致多次标记。现在,我们需要一个新的分类方法,可以把所有合数无遗漏无重复的分类。
考虑一个合数
既然我们要
因为
因为
但是,对
因为每个数都会唯一地分进一个类中,故这个算法是
int prime[60000006], cnt = 0; //cnt记录当前质数个数
bool p[60000005] = {};
void init() {
for (int i = 2; i <= n; i++) {
if (!p[i])
prime[++cnt] = i;
//j <= cnt:只能枚举当前找出来的质数
for (int j = 1; j <= cnt && prime[j] <= n / i; j++) {
p[i * prime[j]] = true;
if (i % prime[j] == 0)
break;
}
}
}
【运用线性筛思想解决问题】
对于一个数的分解质因数早已学过,但现在有了多次询问。
注意到,分解质因数的操作显然是一个可以递归的操作:如果要分解
考虑记录
还是一样的思路:对于一个数
我们把每一个数都分进它最小质因数对应的类中。但是这样还是难以解决,所以我们考虑把每一个数都分进它最大真因数对应的类中。然后在枚举到一个数的最大真因数
可以发现,我们只需要在 p[i * prime[j]] = true
之后加上一句 mn[i * prime[j]] = prime[j]
即可。
因数个数的公式很简单:先分解质因数,然后每个质因数的指数加一再相乘。
但是,我们希望这个公式可以完成递推。
当我们已经知道了
-
中不含 ,那么 ; -
中含 。因为我们的 实际上枚举到 的最小质因数就退出了,所以我们只需要考虑当 乘上了 的最小质因数后的变化。我们只需要同时记录下
的最小质因数的次数 ,就可以方便地让 。(显然 )
经过这么一系列的递推,我们就可以求出所有数的因数个数了。同理,因数和也可以求出来,只是公式不同而已。
【总结】
可以发现,我们在数论上的递推与动态规划时的递推并不相同。我们在数论上的递推通常不是由
【积性函数】
若
例如上面的因数个数就是一个积性函数。
积性函数就意味着我们可以进行递推。
【欧拉函数】
定义
若
因此,我们可以这样求欧拉函数。
如果我们要求
如果一个人位于
于是这个题就变成了问有多少对数互质。当然,我们考虑一半即可,注意对角线只有一个能看到,特殊处理。
考虑对于每一列
【乘法逆元】
在题目中,我们经常需要取模。但是如果涉及除法,乱除可能导致答案错误。这时候,我们尝试用乘法在取模意义上代替除法。于是,乘法逆元诞生了。
若
注意:只有
逆元有什么用?如果我们要在模的意义下做除法,就相当于乘以除数的逆元。
那么如何快速求出逆元?
接下来我们做两件事。
-
若
,求证: 存在且唯一。 -
的时间内求出 。
首先提出两个概念:完全剩余系和简化剩余系。
完全剩余系:简称完系。
简化剩余系:简称简系。
记
下证:若
令
显然,因为
因为逆元就是相乘得一,所以我们考虑把所有东西都乘起来看看。
我们只需证明:对于任意
-
有 个。且由于 ,所以 。 -
对于任意
, 。这是因为 ,矛盾。
因此,
证毕。
怎么求乘法逆元?
欧拉定理:对于
证明:考虑模
所以
记
又
故,若
所以
由于
【连续的乘法逆元】
注意要求模数为质数。
如果对于每一个数,都用
我们希望可以重复利用之前求出来的数据。
假设已知
考虑带余除法:
两边同时乘
所以,
初始设立
带余除法:
如果我们想求
【BSGS】
给定
首先有一个枚举的想法,
然后想到:
一个一个往前枚举太慢了,如果能先大幅度跨,然后再小幅度调整,时间就会快很多。
设
因为
考虑用带余除法的方式转换式子。
我们枚举
如果我们能找到一个
但是,如果不能快速找到
建立一个 map,索引时
【具体实现时的修改】
在具体实现时,我们让
我们让 map[
当然,我们也可以让 map[
至于
【裴蜀定理】
裴蜀定理:
二元形式:若
考虑模
【拓展欧几里得算法】
判断无解:裴蜀定理,
接下来尝试寻找一组整数解。一个简单的想法显然可以枚举
众所周知,辗转相除法可以求出
辗转相除法:不妨
对
这么做之所以好,是因为等式左侧的关系很明显,而右侧始终相等。
同样地,考虑辗转相除法的结束条件:
当我们递归结束时,方程变为
显然此时的有解
现在我们希望通过这一层的解推出上一层的解。
即已知
因为我们想要
现在的方程变为
拆括号,
所以,令
【中国剩余定理】
记
不妨设
有特解:
正确性:
通解:
【阶与原根】
阶:
性质一:若
证明:若
性质二:
由于
如果
原根的性质:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!