算法学习笔记(28): 筛法

算法,学习,笔记,筛法 · 浏览次数 : 12

小编点评

**筛法** 筛法是一种快速计算积性函数的方法。它利用了将积性函数分解成更简单的积性和线性函数的方法来高效计算它们。 **线性筛** 线性筛是一种基本的积性函数计算方法。它基于以下想法: * 积性函数可以表示为两个线性函数的乘积。 * 两个线性函数可以通过卷积运算求解。 * 积性函数的值可以从线性函数中快速计算出来。 **PN 筛** PN 筛是一种改进线性筛的方法,它可以高效地计算积性函数的值。PN 筛的基本思想是利用质数集的性质来构建一个包含所有素数的集合。 **Min25 筛** Min25 筛是一种用于计算积性函数值的方法。它利用了以下步骤: 1. 计算所有小于 \\(2^{25}\\) 的素数。 2. 计算所有与所有素数同余的数字的积性函数值。 3. 遍历所有素数,并为每个素数计算其积性函数值。 **Powerfull Number** Powerfull Number 是一个在素数分解中具有特殊性质的数。如果一个数是 Powerfull Number,则它的质数分解中所有素数的指数都相等。 **如何使用筛法计算积性函数?** 1. 选择一个适当的筛法,例如线性筛或 PN 筛。 2. 计算所有素数。 3. 计算所有与所有素数同余的数字的积性函数值。 4. 遍历所有素数,并为每个素数计算其积性函数值。 **时间复杂度** 线性筛的平均时间复杂度为 \\(O(n\log n)\\),其中 \\(n\\) 是输入数字的大小。PN 筛的平均时间复杂度为 \\(O(n\\log n)\\),而 Min25 筛的平均时间复杂度为 \\(O(n\log n)\\)。

正文

筛法

本文不作为教学向文章。

线性筛

线性筛是个好东西。一般来说,可以在 \(O(n)\) 内处理几乎所有的积性函数。

还可以 \(O(n)\) 找出所有的质数……(废话


杜教筛

放在偏序关系 \((\Z, |)\) 中卷积……

如何快速的求 \(S(n) = \sum_{i = 1}^n f(i)\)

如果能够找到一个函数 \(g\)

\[\begin{aligned} \sum_{i = 1}^n (f * g)(i) &= \sum_{i = 1}^n \sum_{d | i} f(\frac id) g(d) \\ &= \sum_{d = 1}^{n} g(d) \sum_{i = 1}^{\lfloor \frac nd \rfloor} f(i) \\ &= \sum_{d = 1}^{n} g(d) S(\lfloor \frac nd \rfloor) \\ \end{aligned} \]

那么可以得出:

\[g(1)S(n) = \sum_{i = 1}^n (f * g)(i) - \sum_{d = 2}^n g(d) S(\lfloor \frac nd \rfloor) \]

这样我们就可以通过整除分块快速的求出 \(g(1) S(n)\) 了。

当然,需要有很严苛的前提:

  • \(f * g\) 的前缀和是可以快速求的。

  • \(g\) 的前缀和也是可以快速求的。

  • 这里的快速应该是 \(O(1)\),如果是 \(O(\log n)\) 估计也没关系。


例题

  1. 快速求 \(\sum_{i = 1}^n \sum_{j = 1}^n \varphi(\gcd(i, j))\)

  2. 快速求 \(\sum_{i = 1}^n \sum_{j = 1}^n i \cdot j \cdot gcd(i, j)\)

例二中有一个很好的东西,点乘。

其定义有:\((f \cdot g)(n) = f(n) \cdot g(n)\)

\(h\)完全积性函数时,有 \((f \cdot h) * (g \cdot h) = (f * g) \cdot h\)

这里列出一些基本的组合:

  • \(\mu \cdot id_k\)

    \(((\mu \cdot id_k) * id_k)(n) = \sum_{d | n} \mu(d) d^k (\frac nd)^k = n^{k + 1}\sum_{d | n} \mu(d) = \varepsilon(n)\)

  • \(\varphi \cdot id_k\)

    \((\varphi \cdot id_k) * id_k = I\),推导同上。


Min25 筛

本质是对线性筛的扩展……

为了方便,声明一些符号:

  • \(P\) 代表质数集合。

  • \(P_i\) 表示第 \(i\) 个质数。

  • \(minp(i)\) 表示 \(i\) 的最小质因数。

还是求 \(\sum_{i = 1}^n f(i)\)。可以用 Min25 筛需要满足一些条件:

  • \(f(i)\) 是一个低阶多项式。

  • \(f(p^k)\) 可以快速的求解。

现在考虑把每一阶分别计算。


PN 筛

首先定义 Powerful Number

一个数是 Powerful Number 当其质因数分解中 \(n = \prod_{i = 1}^k p_i^{c_i}\)\(\forall i(c_i > 1)\)

由于一个 Powerful Number 可以表示为一个数的平方和一个数的立方,所以 \(n\) 以内的 PN 数一共有 \(O(\sqrt n)\) 个。

现在假设要求 \(F(n) = \sum_{i = 1}^n f(i)\)

需要找到函数 \(g\) 满足:

  • 为积性函数。

  • 易求前缀和。

  • 在质数 \(p\) 处取值与原函数相等:\(f(p) = g(p)\)

\(G(n) = \sum_{i = 1}^n g(n)\)

先构造函数 \(h\) 满足 \(h * g = f\)

于是将卷积展开:

\[\begin{aligned} F(n) &= \sum_{i = 1}^n f(i) = \sum_{i = 1}^n \sum_{d | i} h(d) g(\frac id) \\ &= \sum_{d = 1}^{n} \sum_{i = 1}^{\lfloor \frac nd \rfloor} h(d) g(i) \\ &= \sum_{d = 1}^n h(d) \sum_{i = 1}^{\lfloor \frac nd \rfloor} g(i) \\ &= \sum_{d = 1}^n h(d) G(\lfloor \frac nd \rfloor) \end{aligned} \]

怎么可以套数论分块?

由于积性函数相乘任然为积性函数,所以可知 \(h(1) = 1\)

所以对于质数 \(p\),有 \(f(p) = g(1)h(p) + g(p)h(1) = h(p) + g(p)\)

由于 \(f(p) = g(p)\),所以可知 \(h(p) = 0\)

根据积性函数的性质,可以推导出 \(h(n)\) 只在 \(n\)PN 数的时候才可能不为 \(0\)

所以原式变为:

\[F(n) = \sum_{\text{d is PN}}^{n} h(d) G(\lfloor \frac nd \rfloor) \]

这样,我们只需要求出 \(O(\sqrt n)\)\(h(d)\) 的取值,以及快速算出 \(G(x)\) 的取值即可。

那么我们考虑如何求解 \(h(d)\)

根据 \(f = g * h\)

\[f(p^c) = \sum_{i = 0}^c g(p^i)h(p^{c - i}) \]

稍微移项可以得到:

\[g(1)h(p^c) = f(p^c) - \sum_{i = 1}^c g(p^i)h(p^{c - i}) \\ \downarrow \\ h(p^c) = f(p^c) - \sum_{i = 1}^c g(p^i)h(p^{c - i}) \]

于是可以枚举所有的 \(p\) 以及 \(c\) 求出 \(h(p^c)\) 即可。

此时 \(p\) 的个数视作 \(O(\sqrt n)\),而 \(c\) 的界为 \(O(\log n)\),有由于是 \(\log n\) 转移,所以这部分复杂度为 \(O(\sqrt n \log n^2)\),而且这个上界很宽松。

但是如果我们直接推导出 \(h(p^c)\) 仅与 \(p^c\) 有关的计算公式,那么可能更加优秀,复杂度一般为 \(O(\sqrt n \log n)\)

所以总的复杂度一般为 \(O(\sqrt n + \sqrt n \log n) = O(\sqrt n \log n)\)。非常的优秀。

与算法学习笔记(28): 筛法相似的内容:

算法学习笔记(28): 筛法

筛法 本文不作为教学向文章。 线性筛 线性筛是个好东西。一般来说,可以在 \(O(n)\) 内处理几乎所有的积性函数。 还可以 \(O(n)\) 找出所有的质数……(废话 杜教筛 放在偏序关系 \((\Z, |)\) 中卷积…… 如何快速的求 \(S(n) = \sum_{i = 1}^n f(i)

算法学习笔记(6): 树链剖分

树链剖分 树链剖分是一个很神奇,但是在树上可以完成一些区间操作问题 简单来说,就是把一棵树分成一条条的链,通过维护链上的信息来维护整棵树的信息 基础知识可以参考我的另外一篇博客:算法学习笔记(5): 最近公共祖先(LCA) 这里假设你已经掌握了上述博客中的所有相关知识,并清晰了其背后的原理 性质?发

算法学习笔记(11): 原根

原根 此文相对困难,请读者酌情食用 在定义原根之前,我们先定义其他的一点东西 阶 通俗一点来说,对于 $a$ 在模 $p$ 意义下的阶就是 $a^x \equiv 1 \pmod p$ 的最小正整数解 $x$ 或者说,$a$ 在模 $p$ 意义下生成子群的阶(群的大小) 再或者说,是 $a$ 在模

算法学习笔记(30):Kruskal 重构树

Kruskal 重构树 这是一种用于处理与最大/最小边权相关的一个数据结构。 其与 kruskal 做最小生成树的过程是类似的,我们考虑其过程: 按边权排序,利用并查集维护连通性,进行合并。 如果我们在合并时,新建一个节点,其权值为当前处理的边的权值,并将合并的两个节点都连向新建的节点,那么就可以得

算法学习笔记(3.1): ST算法

ST表 在RMQ(区间最值)问题中,著名的ST算法就是倍增的产物。ST算法可以在 \(O(n \log n)\) 的时间复杂度能预处理后,以 \(O(1)\) 的复杂度在线回答区间 [l, r] 内的最值。 当然,ST表不支持动态修改,如果需要动态修改,线段树是一种良好的解决方案,是 \(O(n)\

C++算法之旅、09 力扣篇 | 常见面试笔试题(上)算法小白专用

算法学习笔记,记录容易忘记的知识点和难题。详解时空复杂度、50道常见面试笔试题,包括数组、单链表、栈、队列、字符串、哈希表、二叉树、递归、迭代、分治类型题目,均带思路与C++题解

C++算法之旅、08 基础篇 | 质数、约数

算法学习笔记,记录容易忘记的知识点和难题。试除法、分解质因数、筛质数、约数个数、约数之和、最大公约数

算法学习笔记(1): 欧几里得算法及其扩展

扩展欧几里得算法详解 在了解扩欧之前我们应该先了解欧几里得算法 欧几里得算法 这是一个递归求最大公约数(greatest common divisor)的方法 $$ gcd(a, b) = gcd(b, a % b) $$ 可以通过一个类似的简单公式推导而来 好像叫做辗转相减法来着? $$ gcd(

算法学习笔记(3): 倍增与ST算法

倍增 目录倍增查找 洛谷P2249重点变式练习快速幂ST表扩展 - 运算扩展 - 区间变式答案倍增更多的用法优化矩形查询优化建图优化 DP作者有话说 倍增,字面意思即”成倍增长“ 他与二分十分类似,都是基于”2“的划分思想 那么具体是怎么样,我们以一个例子来看 查找 洛谷P2249 依据题面,我们知

算法学习笔记(4): 并查集及其优化

# 并查集 并查集,Disjoint-Set,或者通俗一点,叫做MergeFind-Set,是一种可以动态维护若干个不重叠的集合,并支持集合之间的合并与查询的数据结构。 集体来说,并查集支持下列两个操作: - Find,查询元素所属集合 - Merge,将两个元素所属集合合并 一般来说,为了具体实现