数论入门笔记

公式较多,渲染可能较慢,正常现象(

这个憨批将近一年没摸数论知识了,于是重新学习了一遍。为了防止学习第三遍,所以打算写个东西记录一下这次我复习到的(很小一部分)数论知识。

因为我的数论知识非常垃圾,所以出错是很正常的,直接跟我对线即可,我会改的TAT

预计会有这些内容:

如果看到有 p,a,m​ 无端出现且没有定义,则默认定义为 n=impiai​,其中 ai0​。

莫比乌斯反演

虽然大家都会,还是丢一下莫比乌斯函数 μ 的定义。设 n=impiai,其中 ai0

μ(n)={0maxa2(1)mmaxa1

莫反的两大形式:

f(n)=d|ng(d)g(n)=d|nμ(nd)f(d)f(n)=n|dg(d)g(n)=n|dμ(dn)f(d)

证明:

(形式一)

d|nμ(nd)f(d)=d|nμ(nd)p|dg(p)=p|ng(p)d|npμ(d)

考虑 S(n)=d|nμ(d) 的值:假设 n=impiaiai0),S(n)=k(m2k)k(m2k+1)=[m=0]=[n=1]。因此有

d|nμ(nd)f(d)=p|ng(p)d|npμ(d)=p|ng(p)[np=1]=g(n)

证毕。

(形式二)

n|dμ(dn)f(d)=n|dμ(dn)d|pg(p)=n|pg(p)d|pnμ(d)=n|pg(p)[pn=1]=g(n)

证毕。

杜教筛

是一种亚线性筛法,可以做到积性函数求前缀和,但是使用难度不低。

先扔一些前置的东西:

狄利克雷卷积

定义两数论函数的狄利克雷卷积 (fg)(n)=d|nf(d)g(nd)

狄利克雷卷积有交换律、结合律与对加法的分配律。证明可以直接把卷积的定义式写出来,对应项对照一下就行了。

积性函数

对于两数 pq,满足 f(p)f(q)=f(pq) 的函数是积性函数。对于任意两数都有 f(p)f(q)=f(pq) 的函数是完全积性函数。

积性函数满足性质:积性函数 * 积性函数 = 积性函数。证明也是直接展开写一写就行了。

常见的积性函数:

  • 元函数 ϵϵ(n)=[n=1]。(完全积性)
  • 恒等函数 II(n)=1。(完全积性)
  • 单位函数 idid(n)=n。(完全积性)
  • 莫比乌斯函数 μ
  • 欧拉函数 ϕϕ(n)=i=1n[in]
  • 约数个数 dd(n)=i=1n[i | n]
  • 约数和 σσ(n)=i=1ni[i | n]

这些积性函数又有一些性质:

  1. 对于任意 ffϵ=f
  2. μI=ϵ
  3. ϕI=id
  4. ϕ(n)n=d|nμ(d)d

证明:

2 证过了。

3:众所周知 ϕ(n)=i=1mpiai1(pi1),所以有

d|nϕ(d)=i1=0a1i2=0a2im=0amϕ(p1i1p2i2pmim)=(1+(p11)+(p12p1)++(p1a1p1a11))i2=0a2im=0amϕ(p2i2pmim)=p1a1i2=0a2im=0amϕ(p2i2pmim)==p1a1p2a2pmam=n

4:将 3 等式两边卷上一个 μ,则有

ϕIμ=idμϕϵ=idμϕ(n)=d|nμ(d)ndϕ(n)n=d|nμ(d)d

杜教筛

搞了那么多,跟杜教筛有啥关系呢(

假设我们想求一个积性函数 f 的前缀和 S(n)=i=1nf(i)

我们不妨找另一个积性函数 g,使得 fg 是一个很优美的积性函数。我们可以得出:

in(fg)(i)=ind|if(d)g(id)=ing(i)jnif(j)=ing(i)S(ni)

因为我们想求 S(n),所以我们可以带 S(n) 的一项提出来。

g(1)S(n)=in(fg)(i)2ing(i)S(ni)

剩下的 S 递归往下做即可。

一般做杜教筛的时候都会加记忆化(不然时间会假),还会先用线性筛预处理出 n 范围内的 S。时间复杂度 O(n2/3),但是我不知道怎么证。

之所以说杜教筛很难用,是因为需要构造出一个 g 才能使用杜教筛,而这个构造过程可能不太好想。

举个例子:求 S(n)=i=1niμ(i)

为了消掉 i​​,我们不妨令 g=id​​,这样 (fg)=(idμ)id=idϵ​​​​。然后套上面公式即可。上面那一块证明的很多性质可以用来构造另一个积性函数。注意狄利克雷卷积对点积没有结合律。

min_25 筛

声明:此版块下的所有 p 默认为质数。定义 lpf(i)i 的最小质因数。

这个筛比较强,可以解决一些杜教筛解决不了的问题,而且不需要构造,是我这种无脑选手的不二之选。

假设我们想求 S(n)=inf(i)

min_25 筛适用于一些积性函数 f,其中 f(p) 可以用多项式表示,且 f(pk) 很好求值。

min_25 筛的主要思想是先把 f 看成完全积性函数 g,其中 g(p)=f(p)g(n)=if(pi)ai。这也就是为什么要求 f(p) 可以用多项式表示的原因:多项式可以被拆成单项式,而对单项式而言计算 g 是容易的。

但是我们把积性函数当成完全积性来算肯定是有错的,具体来说,在 xprime 的时候会有错。我们先考虑算出质数部分的和。

我们定义 G(i,j)=ing(i)[iprimelpf(i)>pj],其中 pj 是第 j 个质数。

G 的初值很显然,G(i,0)=ing(i),而一般 g 的前缀和是好求的。

可以发现 G(i,|P|)=ing(i)[iprime],因此我们考虑将 j 从小到大递推。

考虑 G(i,j1)G(i,j) 多了些什么。我们发现 G(i,j1)G(i,j)=xig(x)[xprimelpf(x)=pj]

pj2>i 时,G(i,j1)G(i,j)=0

pj2i 时,G(i,j1)G(i,j)=g(pj)(G(ipj,j1)k=1j1g(pk))。之所以要减那一坨是因为 G 里面还包含了质数,但是算了 <pj 的质数的贡献会导致我们乘出来的数的最小质因数不是 pj,因此要把它挖掉。


这样我们就可以递推求出 G(i,|P|) 了,但是我们还需要合数部分的值,于是我们再定义一个 F(i,j)=inf(i)[iprimelpf(i)>pj],不难发现答案就是 F(n,0)+f(1)(1 没算)。

初值也是好得到的,F(i,|P|)=G(i,|P|)

于是我们再考虑将 j 从大到小递推。

考虑 F(i,j1)F(i,j) 多了些什么。我们发现 F(i,j1)F(i,j)=xif(x)[xprimelpf(x)=pj]

但是因为 f​ 不是完全积性,所以我们不能直接向之前一样乘,还得枚举一个指数。

这里定义一个函数 sp(x)=ixf(pi)​,这个可以线性筛求出来,因为我们只用得到 n​ 的质数。​

具体而言,F(i,j1)F(i,j)=e,pjeif(pje)(F(ipje,j)sp(j))+e2,pjeif(pje)

于是快乐递推就行了。

时间复杂度是 O(n0.75logn),据说 min_26 筛可以做到 O(n2/3),但是跑起来差不多,我也不会。


powerful number 筛我不太会,但我在翻 min_25 筛题解的评论区的时候找到了这样一条评论:

所以大家可以问 hs。

二次剩余

一个数 a 是模 p 意义下的二次剩余,当且仅当存在数 x​ 使得 x2a(modp)

这里只讨论 p​ 是奇质数的二次剩余,因为其他情况我不会。

先扔一些有用的性质。以下性质均不考虑 0。

性质

  1. ap121(modp)​ 或 ap121(modp)

由费马小定理,显然。注意我们排除了 0 的情况。

  1. a​ 是二次剩余当且仅当 ap121(modp)​,否则 ap121(modp)​。

因为 p 是奇质数,所以 p 存在原根,设原根之一为 ϕ,则有 ap12ϕbp121(modp)​,可得 b 是偶数。

所以 (ϕb/2)2ϕba(modp)​,a​​​ 是二次剩余,必要性得证。

a 是二次剩余,设 ax2,则 ap12xp11,充分性得证。

又因为性质 1,所以 a​ 不是二次剩余 ap121(modp)​​。​

这玩意又被叫做欧拉判别准则,还有个勒让德符号可以方便的表示这个式子:

(np)=np12modp{1pnn是二次剩余1pnn不是二次剩余0pn

  1. [1,p1]​ 中,二次剩余和非二次剩余各有 p12​ 个。

显然 (px)2x2,因此我们只需证明 x2modp 两两不同就能说明有 p12 个二次剩余。

假设 x2y2(xy)(x+y)0

x+y[2,p1],因此 x=y。得证。

Cipolla 算法

那如果我们现在拿到一个方程 x2n(modp),咋解呢?

我们先随机一个数 a​​​,使得 a2n​​​ 不是二次剩余。类似复数,我们利用 i​​ 扩一个域,其中 i2=a2n​​,我们断言解是 (a+i)p+12 和它的相反数​。

证明:

(a+i)p+1(a+i)(a+i)p(a+i)(ap+ip)(a+i)(ai)a2i2n

第三个同余:apap1aa,ipip1ii。注意因为 i2 不是二次剩余,所以 (i2)p12ip11

现在我们唯一的问题就是,(a+i)p+12 的虚部会不会非零?

假设 (a+i)p+12A+Bi,则 (a+i)p+1A2+B2(a2n)+2ABin ,所以 2AB0

A0,有 B2(a2n)n,而 (B2(a2n))p12Bp1(a2n)p121,而因为 n 是二次剩余,有 np121,矛盾。所以 A0,则 B0

exBSGS

普通的 BSGS 只能做底数模数互质的问题,但是可能会有憨批出题人出成非质模数,这时候就只能用 exBSGS了,,,

但是正规的 exBSGS 实在是太麻烦了,所以我乱搞了一个奇怪的算法(

还是和正常的 BSGS 一样,先预处理出 axmod​,但是注意这里每个值的出现次数要记录最小和次小,因为两个不互质的值取模之后的结果是一个 ρ 形。

然后再枚举每个 ay,查询有没有 x 满足 axmodbay。但是因为底数模数并不互质,所以 a 没有逆元,即 axmodyb 并不一定成立,所以需要写个快速幂验证一下。然后就行了。

这玩意具体有没有正确性我也不知道(),但是把模板题过了,应该问题不大(

万能欧几里得算法

这位更是重量级。

万能欧几里得算法是用来解决一系列与 ax+bc​​ 有关的式子,要求满足操作(值)可以合并。

其主要思想是观察直线 y=ax+bc​ 在一象限的图像:

每当其碰到一条竖线(x=a(a>0))时记录一个操作 R,碰到一条横线(y=a(a>0))时记录一个操作 U,碰到一个整点时记录两个操作 UR

比如我们现在要求 x=1nax+bc​​​,我们就可以记录两个值 cnt,s​​​,当扫描到一个操作 U​​​ 时将 cnt+1​​​,扫描到 R​​​ 时将 s+cnt​​​,最后的 s​​ 就是答案。

做万欧的时候,每个问题都会被看成一个节点 f(a,b,c,n,U,R),表示我们想求关于 ax+bc 的某个式子,其中的 x(0,n] 的整值,碰到横线的操作序列是 U,碰到竖线的操作序列是 R。我们发现 aca<c 的问题好像有一些不一样,所以我们将其分类讨论。

ac​ 时,我们发现 ax+bc=(a mod c)x+bc+acx​,这说明每一个 R​ 操作之前至少会有连续的 ac​ 个 U​(换句话说就是 x+1​,y​ 至少加 ac​),于是我们可以将 R​ 变成 UacR​,且把 a​ 变为 amodc​。形式化的说,ac​ 时,f(a,b,c,n,U,R)=f(amodc,b,c,n,U,UacR)​​。​这样我们就成功的把数据规模从 max(a,c)​ 变成了 c​。注意这里的合并操作有结合律,但是没有交换律。

a<c​ 时我们发现我们没办法再用上面的方法减少数据规模了,但是我们可以借鉴 gcd 和 exgcd 的思路,考虑交换 a,c​。

具体而言,我们把我们的直线改写一下:y=ax+bcx=cyba,我们发现此时 a,c 交换了地位。这看起来很不错,好像可以直接把函数一变,把 UR 互换,把 nR 的个数变成 U 的个数来达到交换的目的,但是我们发现有一些细节问题:

  • 我们要求碰到整点时操作序列应该加上一个 UR​,但是 RU 交换之后加的顺序反了。
  • 我们的直线是从 (0,bc) 开始走的,我们利用了这个点 x=0 的性质,但是转化之后的起点并没有这个性质。
  • x=n 时,y 并不一定取得到整值。这会导致 n 不能直接变换。

我们考虑一条一条解决这些问题。

  • 我们摸到一个整点时是将其视为先摸到横线后摸到竖线的,但这里既然横竖交换了那我们就考虑先让其先摸到竖线后摸到横线:我们直接把新的直线往左平移 1a,变成 x=cyb1a,这样可以解决这个问题,虽然这个方案听起来很傻逼。

  • 我们没办法让起始点的 y=0,那么我们可以考虑找到直线经过的第一条横线,将其对应的 U 以及之前的所有 R 全部单独加上去,剩下的横竖翻转转成一个子问题。这样方程就变成了 x=c(y+1)b1a=cy+cb1a,因为第一个 U 被删掉了,每个 U 的序号都 -1 了。

  • 我们没办法让结束点的 y​ 取整值,那我们就把最后一个 U​ 后面的所有 R​ 全部取下来单独算,然后同上。直线方程不变。

于是我们发现 f(a,b,c,n,U,R)=Rcnt1U+f(c,(cb1)moda,a,cntU1,R,U)+Rcnt2。推一下这几个常量式子发现:

  • cntU=an+bc(其实就是把 x=n 带进去的 y 值向下取整),-1 是因为第一个 U 被干掉了;
  • cnt1=cb1a​,把 y=0​​ 代入新的直线方程即可得到。注意要带新的方程,而且区间是开区间,不然若直线过的第一个 U​ 对应的是整点,会出现该 U​ 后面的那个 R​ 也被算进去的情况。
  • cnt2=nccntUb1a,把 y=m1 代入新的直线方程即可得到。注意要带新的方程,而且区间是开区间,理由与上面类似。

后两条可以自己试着画画图理解一下,发现他们完美的避开了所有不该取的点而对该取的点没影响。


但是我知道这个之后怎么做题呢?

假设我还是想求 x=1nax+bc​,我们定义一个结构体“状态”,一个状态里面记录三个数 cntx,cnty,s​,分别为 R​ 的个数,U​​ 的个数与我们已经求到的和。

我们考虑合并两个状态 S,T

  • ans.cntx=S.cntx+T.cntx
  • ans.cnty=S.cnty+T.cnty
  • ans.s=S.s+T.s+S.cntyT.cntx,最后一项的意义是把 T 拼在 S 后面相当于给 T 的每一项加上一个 S.cnty,而 TT.cntx 项。

最后我们只需要考虑初始时候 U,R​ 的值。我们想要的是扫到 U 的时候 cnty+1,扫到 R 的时候 cntx+1,s+cnty​。因此不难推出初始 Ucnty=1,初始 Rcntx=1,其他值 =0。​

于是我们做完了。类似的我们可以求出 x=1nax+bc2​​​​,x=1nxax+bc​​​​,甚至可以往上面套一堆矩阵或者数据结构等。

万欧的时间复杂度是 O(Tlogmax(a,c))T 是合并两个信息的时间:我们需要时间的地方只有每一层的 cnt1,cnt2ac。容易发现 cnt1,cnt2ca,也就是上一层的 ac,所以每层所需的时间就是 O(T(logalogc)),而 c 又会在下一层变成分子,所以相邻两项的加减抵消,时间复杂度变成 O(Tlogmax(a,c))。当然这只是比较理性的感性理解(

完结撒花ᕕ( ᐛ )ᕗ

posted @   恨妹不成穹  阅读(193)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示