[学习笔记]min_25筛
模板测试链接
〇、前言
与杜教筛相似的是,\(\tt min\_25\) 筛也是用于计算积性函数的前缀和的,有一些前置芝士与杜教筛相似,如果忘记先去看一看杜教筛吧.
\(\tt min\_25\) 筛主要适用在 \(f(p^k)\) 较好求(\(p\) 为质数),并且对于 \(f\) 可以拆成多个完全积性函数的情况.
壹、推导思路
设 \(f\) 是一个积性函数,同时定义 \(F(n)=\sum_{i=1}^nf(i)\),我们的目的就是求得 \(F(n)\),\(\tt min\_25\) 筛的主要思路是将前 \(n\) 个数分成 质数、合数、\(1\) 三个部分,我们定义 \(\Bbb P\) 为质数集,\(\Bbb C\) 为合数集,那么
由于合数最后可以被质数表示,我们先从合数入手……
一、计算合数
合数部分是 \(\sum_{i\in \Bbb C,i\le n}f(i)\).
由于每个合数都可以用质数表示,我们考虑枚举每个合数的最小质因数,将 \(\sum_{i\in \Bbb C,i\le n}f(i)\) 换个写法
同时,我们这里的 \(x\) 要保证其最小质因数必须比 \(p\) 更大.
我们定义 \(S(i,j)\) 表示对于所有 \(1<x\le i\) 的,最小质因数 \(p>j\) 的 \(f(x)\) 之和,我们考虑将 \(S\) 也分成质数、合数进行计算,记 \(G(n)=\sum_{i\in \Bbb P,i\le n}f(i)\),那么我们可以将 \(S(i,j)\) 换一种形式
后面那个循环中,必须保证 \(x\) 的最小质因数比 \(p\) 更大.
发现是同样的形式,那么就是
如果我们知道了 \(G()\),那么我们就可以递归求得 \(S()\) 了.
我们考虑一下 \(S\) 的边界情况,如果 \(i\le j\),那么 \(S(i,j)=0\).
同时,注意到是否有 \(S(n,0)=S(n,1)=F(n)-f(1)\) 这个等式?
那么,我们可以将最后的答案换个写法,有
我们现在唯一需要的,就是如何求 \(G()\) 了.
注意,我们这里的 \(S(i,j)\) 中的 \(j\) 并不是质数下标,而是质数本身,为了防止混乱,所以就这样了.
在这个定义体系下,其实这个 \(j\) 为质数才有意义.
二、如何求 G()
假定我们要求的函数 \(f\) 可以写成 \(f(x)=a_0+a_1x+a_2x^2+...+a_tx^t\),那么我们再定义函数 \(f_i=x^i\),不难发现,对于每一个 \(f_i\) 都是完全积性函数,同时,有 \(f(x)=\sum_{i=0}a_if_i(x)\),所以我们只需要将每个 \(f_i\) 单独算出来,最后带上系数就可以求得最终的 \(f(x)\) 的值.
但是由于我们已知的 \(f(x)\) 是在 \(x\in \Bbb P\) 的情况下的表达式,我们无法算出 \(x\) 不是质数的情况,但是对于我们要求 \(G()\) 而言,是无所谓的,因为我们求的 \(G()\) 只包含质数,对于其他数而言,虽然我们中途可能会计算出一些假的 \(f()\) 的值,但是最后会被同样地筛掉,对于最终结果没有影响.
同时,由于有了这个的影响,我们的 \(S()\) 也只会单个求每个 \(f\) 的值,然后加起来.
考虑怎么求 \(G()\),我们定义
其中 \(\text{minp}_x\) 表示 \(x\) 的最小质因数.
接下来我们将下标 \(t\) 省略不写,但是需要明白我们其实一直都只在求原 \(f\) 的一个部分.
对于 \(g\) 的这个定义,我们不难发现 \(G(n)=g(n,\sqrt n)\),或者说 \(G(n)=g(n,x)\),其中 \(x\) 为小于 \(\sqrt n\) 的最大质数.
考虑 \(g(i,j)\) 的转移,设 \(j'\) 为小于 \(j\) 的最大的质数.
当 \(i<j^2\) 时,质数 \(j\) 筛不去任何质数,这个时候可以直接转移,即
当 \(i\ge j^2\) 时,从 \(g(i,j')\) 到 \(g(i,j)\),我们筛掉了以 \(j\) 为最小质因数的那些数字,由于 \(f\) 是个完全积性,我们考虑将 \(f(j)\) 这个部分提出来,然后就有
为什么后面还要减去 \(\sum_{k\in \Bbb P}^{k<j}f(k)\) 这个部分?由于我们的 \(g()\) 包含了上界以内的所有质数,对于需要保证最小质因数为 \(j\) 而言,这一部分是多余的,因而需要去掉.
初始状态有
对于 \(k\notin \Bbb P\) 而言,这里的 \(f\) 算出来是假的,但是我们最后使用的都是只含质数的值,对答案没有影响.
注:由于我们的 \(S(i,j)\) 与 \(g(i,j)\) 中的第二维,也就是 \(j\),其实都只会是质数(否则没什么意义),所以我们可以考虑将 \(\sqrt n\) 以内的质数全部处理出来,然后对于 \(j\) 我们不再传质数,而是选择传质数对应在质数表中的下标,这样,原来意义上的 \(j’\) 就是 \(j-1\),因为他们在质数表中的位置仅相差一,同时我们在转移中使用的 \(j\) 就是 \(\text{prime}_j\),并且 \(S(i,0)\neq S(i,1)\),且 \(g(i,0)\neq g(i,1)\).
三、整理
\(S(i,j)\) 表示对于所有 \(1<x\le i\) 的,最小质因数 \(p>\text{prime}_j\) 的 \(f(x)\) 之和.
定义
\[g_t(i,j)=\sum_{x\in \Bbb P\; or\; \text{minp}_x>\text{prime}_j}^{x\le i}f_t(x)\quad (j\in \Bbb P) \]其中 \(\text{minp}_x\) 表示 \(x\) 的最小质因数.
\(g(i,j)\) 的含义是 \([1,i]\) 中的质数的 \(f(x)\),或者最小质因数大于 \(\text{prime}_j\) 的数的 \(f(x)\) 的值.
有 \(F(n)=\sum_{i=1}^nf(i)\),并且有
定义 \(P\) 表示小于等于 \(\sqrt n\) 的最大质数.
对于 \(S(i,j)\),我们有
其实 \(g(i,P)=G(i)\),只是换了个写法.
对于 \(g(i,j)\),我们有
\(\tt min\_25\) 筛适用的函数 \(f\) 满足以下特性:
- \(f\) 是积性函数;
- \(n\le 10^{10}\);
- \(f(p)\) 是多项式函数(\(p\in \Bbb P\));
- \(f(p^k)\) 可以较为快速地求出 (\(p\in \Bbb P\));
四、时间复杂度
结论是 \(\mathcal O(\frac{n^{3\over 4}}{\ln n})\),能用就行.
贰、实现细节
由于我们最后只求 \(S(n,0)\),所以我们会调用 \(g(\left\lfloor \frac{n}{i}\right\rfloor,P)\),一共就 \(2\sqrt n\) 个数值,考虑分情况进行 \(\tt hash\),即如果 \(\left\lfloor \frac{n}{i}\right\rfloor\le \sqrt n\),那么将编号存到 \(\tt id1[\left\lfloor \frac{n}{i}\right\rfloor]\) 中,如果 \(\left\lfloor \frac{n}{i}\right\rfloor>\sqrt n\),那么将编号存到 \(\tt id2[\left\lfloor \frac{n}{\frac{n}{i}}\right\rfloor]\) 中,总而言之,我们要让下标在 \(\sqrt n\) 的范围之内,这样才能用更小的空间存下标号.
并且,由于 \(g(i,j)\) 的 \(j\) 一直都是 \(P\),所以代码实现时只需要一维就可以了.
而这一维我们将其离散了,放在 \(\tt id1,id2\) 里面.