公式较多,渲染可能较慢,正常现象(
这个憨批将近一年没摸数论知识了,于是重新学习了一遍。为了防止学习第三遍,所以打算写个东西记录一下这次我复习到的(很小一部分)数论知识。
因为我的数论知识非常垃圾,所以出错是很正常的,直接跟我对线即可,我会改的TAT
预计会有这些内容:
如果看到有 p,a,m 无端出现且没有定义,则默认定义为 n=∏i≤mpaii,其中 ai≠0。
虽然大家都会,还是丢一下莫比乌斯函数 μ 的定义。设 n=∏i≤mpaii,其中 ai≠0。
μ(n)={0maxa≥2(−1)mmaxa≤1
莫反的两大形式:
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=∏i≤mpaii(ai≠0),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)
证毕。
是一种亚线性筛法,可以做到积性函数求前缀和,但是使用难度不低。
先扔一些前置的东西:
狄利克雷卷积
定义两数论函数的狄利克雷卷积 (f∗g)(n)=∑d|nf(d)g(nd)。
狄利克雷卷积有交换律、结合律与对加法的分配律。证明可以直接把卷积的定义式写出来,对应项对照一下就行了。
积性函数
对于两数 p⊥q,满足 f(p)f(q)=f(pq) 的函数是积性函数。对于任意两数都有 f(p)f(q)=f(pq) 的函数是完全积性函数。
积性函数满足性质:积性函数 * 积性函数 = 积性函数。证明也是直接展开写一写就行了。
常见的积性函数:
- 元函数 ϵ:ϵ(n)=[n=1]。(完全积性)
- 恒等函数 I:I(n)=1。(完全积性)
- 单位函数 id:id(n)=n。(完全积性)
- 莫比乌斯函数 μ。
- 欧拉函数 ϕ:ϕ(n)=∑ni=1[i⊥n]。
- 约数个数 d:d(n)=∑ni=1[i | n]。
- 约数和 σ:σ(n)=∑ni=1i⋅[i | n]。
这些积性函数又有一些性质:
- 对于任意 f,f∗ϵ=f。
- μ∗I=ϵ。
- ϕ∗I=id。
- ϕ(n)n=∑d|nμ(d)d。
证明:
2 证过了。
3:众所周知 ϕ(n)=∏mi=1pai−1i(pi−1),所以有
∑d|nϕ(d)=a1∑i1=0a2∑i2=0⋯am∑im=0ϕ(pi11pi22⋯pimm)=(1+(p1−1)+(p21−p1)+⋯+(pa11−pa1−11))a2∑i2=0⋯am∑im=0ϕ(pi22⋯pimm)=pa11a2∑i2=0⋯am∑im=0ϕ(pi22⋯pimm)=⋯=pa11pa22⋯pamm=n
4:将 3 等式两边卷上一个 μ,则有
ϕ∗I∗μ=id∗μϕ∗ϵ=id∗μϕ(n)=∑d|nμ(d)⋅ndϕ(n)n=∑d|nμ(d)d
杜教筛
搞了那么多,跟杜教筛有啥关系呢(
假设我们想求一个积性函数 f 的前缀和 S(n)=∑ni=1f(i)。
我们不妨找另一个积性函数 g,使得 f∗g 是一个很优美的积性函数。我们可以得出:
∑i≤n(f∗g)(i)=∑i≤n∑d|if(d)g(id)=∑i≤ng(i)∑j≤⌊ni⌋f(j)=∑i≤ng(i)S(⌊ni⌋)
因为我们想求 S(n),所以我们可以带 S(n) 的一项提出来。
g(1)S(n)=∑i≤n(f∗g)(i)−∑2≤i≤ng(i)S(⌊ni⌋)
剩下的 S 递归往下做即可。
一般做杜教筛的时候都会加记忆化(不然时间会假),还会先用线性筛预处理出 √n 范围内的 S。时间复杂度 O(n2/3),但是我不知道怎么证。
之所以说杜教筛很难用,是因为需要构造出一个 g 才能使用杜教筛,而这个构造过程可能不太好想。
举个例子:求 S(n)=∑ni=1iμ(i)。
为了消掉 i,我们不妨令 g=id,这样 (f∗g)=(id⋅μ)∗id=id⋅ϵ。然后套上面公式即可。上面那一块证明的很多性质可以用来构造另一个积性函数。注意狄利克雷卷积对点积没有结合律。
声明:此版块下的所有 p 默认为质数。定义 lpf(i) 为 i 的最小质因数。
这个筛比较强,可以解决一些杜教筛解决不了的问题,而且不需要构造,是我这种无脑选手的不二之选。
假设我们想求 S(n)=∑i≤nf(i)。
min_25 筛适用于一些积性函数 f,其中 f(p) 可以用多项式表示,且 f(pk) 很好求值。
min_25 筛的主要思想是先把 f 看成完全积性函数 g,其中 g(p)=f(p),g(n)=∏if(pi)ai。这也就是为什么要求 f(p) 可以用多项式表示的原因:多项式可以被拆成单项式,而对单项式而言计算 g 是容易的。
但是我们把积性函数当成完全积性来算肯定是有错的,具体来说,在 x∉prime 的时候会有错。我们先考虑算出质数部分的和。
我们定义 G(i,j)=∑i≤ng(i)[i∈prime∨lpf(i)>pj],其中 pj 是第 j 个质数。
G 的初值很显然,G(i,0)=∑i≤ng(i),而一般 g 的前缀和是好求的。
可以发现 G(i,|P|)=∑i≤ng(i)[i∈prime],因此我们考虑将 j 从小到大递推。
考虑 G(i,j−1) 比 G(i,j) 多了些什么。我们发现 G(i,j−1)−G(i,j)=∑x≤ig(x)[x∉prime∧lpf(x)=pj]。
当 pj2>i 时,G(i,j−1)−G(i,j)=0。
当 pj2≤i 时,G(i,j−1)−G(i,j)=g(pj)(G(⌊ipj⌋,j−1)−∑j−1k=1g(pk))。之所以要减那一坨是因为 G 里面还包含了质数,但是算了 <pj 的质数的贡献会导致我们乘出来的数的最小质因数不是 pj,因此要把它挖掉。
这样我们就可以递推求出 G(i,|P|) 了,但是我们还需要合数部分的值,于是我们再定义一个 F(i,j)=∑i≤nf(i)[i∈prime∨lpf(i)>pj],不难发现答案就是 F(n,0)+f(1)(1 没算)。
初值也是好得到的,F(i,|P|)=G(i,|P|)。
于是我们再考虑将 j 从大到小递推。
考虑 F(i,j−1) 比 F(i,j) 多了些什么。我们发现 F(i,j−1)−F(i,j)=∑x≤if(x)[x∉prime∧lpf(x)=pj]。
但是因为 f 不是完全积性,所以我们不能直接向之前一样乘,还得枚举一个指数。
这里定义一个函数 sp(x)=∑i≤xf(pi),这个可以线性筛求出来,因为我们只用得到 ≤√n 的质数。
具体而言,F(i,j−1)−F(i,j)=∑e,pej≤if(pej)(F(⌊ipej⌋,j)−sp(j))+∑e≥2,pej≤if(pej)。
于是快乐递推就行了。
时间复杂度是 O(n0.75logn),据说 min_26 筛可以做到 O(n2/3),但是跑起来差不多,我也不会。
powerful number 筛我不太会,但我在翻 min_25 筛题解的评论区的时候找到了这样一条评论:

所以大家可以问 hs。
一个数 a 是模 p 意义下的二次剩余,当且仅当存在数 x 使得 x2≡a(modp)。
这里只讨论 p 是奇质数的二次剩余,因为其他情况我不会。
先扔一些有用的性质。以下性质均不考虑 0。
性质
- ap−12≡1(modp) 或 ap−12≡−1(modp)
由费马小定理,显然。注意我们排除了 0 的情况。
- a 是二次剩余当且仅当 ap−12≡1(modp),否则 ap−12≡−1(modp)。
因为 p 是奇质数,所以 p 存在原根,设原根之一为 ϕ,则有 ap−12≡ϕbp−12≡1(modp),可得 b 是偶数。
所以 (ϕb/2)2≡ϕb≡a(modp),a 是二次剩余,必要性得证。
若 a 是二次剩余,设 a≡x2,则 ap−12≡xp−1≡1,充分性得证。
又因为性质 1,所以 a 不是二次剩余 ⇔ ap−12≡−1(modp)。
这玩意又被叫做欧拉判别准则,还有个勒让德符号可以方便的表示这个式子:
(np)=np−12modp≡⎧⎨⎩1p∤n∧n是二次剩余−1p∤n∧n不是二次剩余0p∣n
- 在 [1,p−1] 中,二次剩余和非二次剩余各有 p−12 个。
显然 (p−x)2≡x2,因此我们只需证明 x2modp 两两不同就能说明有 p−12 个二次剩余。
假设 x2≡y2,(x−y)(x+y)≡0。
而 x+y∈[2,p−1],因此 x=y。得证。
Cipolla 算法
那如果我们现在拿到一个方程 x2≡n(modp),咋解呢?
我们先随机一个数 a,使得 a2−n 不是二次剩余。类似复数,我们利用 i 扩一个域,其中 i2=a2−n,我们断言解是 (a+i)p+12 和它的相反数。
证明:
(a+i)p+1≡(a+i)(a+i)p≡(a+i)(ap+ip)≡(a+i)(a−i)≡a2−i2≡n
第三个同余:ap≡ap−1a≡a,ip≡ip−1i≡−i。注意因为 i2 不是二次剩余,所以 (i2)p−12≡ip−1≡−1。
现在我们唯一的问题就是,(a+i)p+12 的虚部会不会非零?
假设 (a+i)p+12≡A+Bi,则 (a+i)p+1≡A2+B2(a2−n)+2ABi≡n ,所以 2AB≡0。
若 A≡0,有 B2(a2−n)≡n,而 (B2(a2−n))p−12≡Bp−1(a2−n)p−12≡−1,而因为 n 是二次剩余,有 np−12≡1,矛盾。所以 A≢0,则 B≡0。
普通的 BSGS 只能做底数模数互质的问题,但是可能会有憨批出题人出成非质模数,这时候就只能用 exBSGS了,,,
但是正规的 exBSGS 实在是太麻烦了,所以我乱搞了一个奇怪的算法(
还是和正常的 BSGS 一样,先预处理出 ax√mod,但是注意这里每个值的出现次数要记录最小和次小,因为两个不互质的值取模之后的结果是一个 ρ 形。
然后再枚举每个 ay,查询有没有 x 满足 ax√mod≡bay。但是因为底数模数并不互质,所以 a 没有逆元,即 ax√mod−y≡b 并不一定成立,所以需要写个快速幂验证一下。然后就行了。
这玩意具体有没有正确性我也不知道(),但是把模板题过了,应该问题不大(
这位更是重量级。
万能欧几里得算法是用来解决一系列与 ⌊ax+bc⌋ 有关的式子,要求满足操作(值)可以合并。
其主要思想是观察直线 y=ax+bc 在一象限的图像:

每当其碰到一条竖线(x=a(a>0))时记录一个操作 R,碰到一条横线(y=a(a>0))时记录一个操作 U,碰到一个整点时记录两个操作 UR。
比如我们现在要求 ∑nx=1⌊ax+bc⌋,我们就可以记录两个值 cnt,s,当扫描到一个操作 U 时将 cnt+1,扫描到 R 时将 s+cnt,最后的 s 就是答案。
做万欧的时候,每个问题都会被看成一个节点 f(a,b,c,n,U,R),表示我们想求关于 ⌊ax+bc⌋ 的某个式子,其中的 x 取 (0,n] 的整值,碰到横线的操作序列是 U,碰到竖线的操作序列是 R。我们发现 a≥c 和 a<c 的问题好像有一些不一样,所以我们将其分类讨论。
a≥c 时,我们发现 ⌊ax+bc⌋=⌊(a mod c)x+bc⌋+⌊ac⌋x,这说明每一个 R 操作之前至少会有连续的 ⌊ac⌋ 个 U(换句话说就是 x+1,y 至少加 ⌊ac⌋),于是我们可以将 R 变成 U⌊ac⌋R,且把 a 变为 amodc。形式化的说,a≥c 时,f(a,b,c,n,U,R)=f(amodc,b,c,n,U,U⌊ac⌋R)。这样我们就成功的把数据规模从 max(a,c) 变成了 c。注意这里的合并操作有结合律,但是没有交换律。
a<c 时我们发现我们没办法再用上面的方法减少数据规模了,但是我们可以借鉴 gcd 和 exgcd 的思路,考虑交换 a,c。
具体而言,我们把我们的直线改写一下:y=ax+bc⇔x=cy−ba,我们发现此时 a,c 交换了地位。这看起来很不错,好像可以直接把函数一变,把 U 和 R 互换,把 n 从 R 的个数变成 U 的个数来达到交换的目的,但是我们发现有一些细节问题:
- 我们要求碰到整点时操作序列应该加上一个 UR,但是 RU 交换之后加的顺序反了。
- 我们的直线是从 (0,bc) 开始走的,我们利用了这个点 x=0 的性质,但是转化之后的起点并没有这个性质。
- 当 x=n 时,y 并不一定取得到整值。这会导致 n 不能直接变换。
我们考虑一条一条解决这些问题。
-
我们摸到一个整点时是将其视为先摸到横线后摸到竖线的,但这里既然横竖交换了那我们就考虑先让其先摸到竖线后摸到横线:我们直接把新的直线往左平移 1a,变成 x=cy−b−1a,这样可以解决这个问题,虽然这个方案听起来很傻逼。
-
我们没办法让起始点的 y=0,那么我们可以考虑找到直线经过的第一条横线,将其对应的 U 以及之前的所有 R 全部单独加上去,剩下的横竖翻转转成一个子问题。这样方程就变成了 x=c(y+1)−b−1a=cy+c−b−1a,因为第一个 U 被删掉了,每个 U 的序号都 -1 了。
-
我们没办法让结束点的 y 取整值,那我们就把最后一个 U 后面的所有 R 全部取下来单独算,然后同上。直线方程不变。
于是我们发现 f(a,b,c,n,U,R)=Rcnt1U+f(c,(c−b−1)moda,a,cntU−1,R,U)+Rcnt2。推一下这几个常量式子发现:
- cntU=⌊an+bc⌋(其实就是把 x=n 带进去的 y 值向下取整),-1 是因为第一个 U 被干掉了;
- cnt1=⌊c−b−1a⌋,把 y=0 代入新的直线方程即可得到。注意要带新的方程,而且区间是开区间,不然若直线过的第一个 U 对应的是整点,会出现该 U 后面的那个 R 也被算进去的情况。
- cnt2=n−⌊c⋅cntU−b−1a⌋,把 y=m−1 代入新的直线方程即可得到。注意要带新的方程,而且区间是开区间,理由与上面类似。
后两条可以自己试着画画图理解一下,发现他们完美的避开了所有不该取的点而对该取的点没影响。
但是我知道这个之后怎么做题呢?
假设我还是想求 ∑nx=1⌊ax+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.cnty∗T.cntx,最后一项的意义是把 T 拼在 S 后面相当于给 T 的每一项加上一个 S.cnty,而 T 有 T.cntx 项。
最后我们只需要考虑初始时候 U,R 的值。我们想要的是扫到 U 的时候 cnty+1,扫到 R 的时候 cntx+1,s+cnty。因此不难推出初始 U 的 cnty=1,初始 R 的 cntx=1,其他值 =0。
于是我们做完了。类似的我们可以求出 ∑nx=1⌊ax+bc⌋2,∑nx=1x⌊ax+bc⌋,甚至可以往上面套一堆矩阵或者数据结构等。
万欧的时间复杂度是 O(Tlogmax(a,c)),T 是合并两个信息的时间:我们需要时间的地方只有每一层的 cnt1,cnt2 和 ⌊ac⌋。容易发现 cnt1,cnt2≤⌊ca⌋,也就是上一层的 ⌊ac⌋,所以每层所需的时间就是 O(T(loga−logc)),而 c 又会在下一层变成分子,所以相邻两项的加减抵消,时间复杂度变成 O(Tlogmax(a,c))。当然这只是比较理性的感性理解(
完结撒花ᕕ( ᐛ )ᕗ
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现