1 条题解
-
0
自动搬运
来自洛谷,原作者为

lcjqwq
赠你满天星搬运于
2025-08-24 21:57:45,当前版本为作者最后更新于2018-11-27 17:56:52,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
杜教筛模板
杜教筛是用来干蛤的呢?
它可以在非线性时间内求积性函数前缀和。
前置知识
积性函数
积性函数:对于任意互质的整数 有 则称 的数论函数。
完全积性函数:对于任意整数 有 的数论函数。
- 常见的积性函数:
- 常见的完全积性函数:
这里特殊解释一下 分别是什么意思:
狄利克雷卷积
设 是两个数论函数,它们的狄利克雷卷积卷积是:$(f*g)(n) = \sum \limits _{d | n} f(d) g(\frac{n}{d})$
性质:满足交换律,结合律
单位元: (即 )
结合狄利克雷卷积得到的几个性质:
莫比乌斯反演
若
则
证明:这里需要用到前面提到的性质:
给出的条件等价于
所以 即 即 结论。
杜教筛
设现在要求积性函数 的前缀和, 设 。
再找一个积性函数 ,则考虑它们的狄利克雷卷积的前缀和
$$\begin{aligned} &= \sum\limits_{i=1}^{n} \sum \limits _{d|i} f(d)g(\frac{i}{d}) \\ &= \sum \limits _{d=1}^{n} g(d)\sum\limits _{i=1}^{\lfloor \frac{n}{d}\rfloor } f(i) \\ &= \sum \limits _{d=1}^{n} g(d) S(\lfloor \frac{n}{d} \rfloor) \end{aligned} $$其中得到第一行是根据狄利克雷卷积的定义。
得到第二行则是先枚举 提出 。
得到第三行则是把 $\sum\limits _{i=1}^{\lfloor \frac{n}{d}\rfloor } f(i) $ 替换为
接着考虑 等于什么。
可以发现,他就等于
$$\sum \limits _{i=1}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor) - \sum \limits _{i=2}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor) $$(可以理解成从1开始的前缀和减去从2开始的前缀和就是第一项)
前面这个式子$\sum \limits _{i=1}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor)$ ,根据刚才的推导,他就等于
所以得到杜教筛的核心式子:
$$g(1)S(n)=\sum\limits_{i=1}^{n}(f*g)(i) - \sum \limits _{i=2}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor) $$得到这个式子之后有什么用呢?
现在如果可以找到一个合适的积性函数 ,使得可以快速算出 和 的前缀和,便可以用数论分块递归地求解。
代码按照理解大概可以写成这样(默认
ll为long long) (可以理解成一个伪代码。。就是一个思路的框架)ll GetSum(int n) { // 算 f 前缀和的函数 ll ans = f_g_sum(n); // 算 f * g 的前缀和 // 以下这个 for 循环是数论分块 for(ll l = 2, r; l <= n; l = r + 1) { // 注意从 2 开始 r = (n / (n / l)); ans -= (g_sum(r) - g_sum(l - 1)) * GetSum(n / l); // g_sum 是 g 的前缀和 // 递归 GetSum 求解 } return ans; }这个代码的复杂度是 ,证明如下:
设求出 的复杂度是 ,要求出 需要求出 个 的值,结合数论分块的复杂度 可得:
$$T(n) = \sum\limits_{i=1}^{\sqrt n} O(\sqrt i) + O(\sqrt {\frac{n}{i}})=O(n^{\frac{3}{4}}) $$还可以进一步优化杜教筛,即先线性筛出前 个答案,之后再用杜教筛。这个优化之后的复杂度是:
$$T(n) = \sum\limits_{i=1}^{\lfloor \frac{n}{m} \rfloor} \sqrt \frac{n}{i} = O({\frac{n}{\sqrt m}}) $$当 时,
可以使用哈希表来存下已经求过的答案,也可以不用。
考虑到上面的求和过程中出现的都是 。开一个大小为两倍 的数组 记录答案。如果现在需要求出
GetSum(x),若 ,返回dp[x],否则返回dp[sqrt n + n / i]即可。这样可以省去哈希表的复杂度。
实战演练
再挂一次核(tao)心(lu)式,全都要靠它:
$$g(1)S(n)=\sum\limits_{i=1}^{n}(f*g)(i) - \sum \limits _{i=2}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor) $$它的关键就是要找到合适的 使得这个东西可以快速地算。
理论知识大概就这么多,接下来看几个例子:
(1) 的前缀和
考虑到莫比乌斯函数的性质 ,自然想到取 。
其中 的前缀和和 的前缀和都弱到爆了。。
所以就轻松的解决了。
杜教筛代码:
inline ll GetSumu(int n) { if(n <= N) return sumu[n]; // sumu是提前筛好的前缀和 if(Smu[n]) return Smu[n]; // 记忆化 ll ret = 1ll; // 单位元的前缀和就是 1 for(int l = 2, r; l <= n; l = r + 1) { r = n / (n / l); ret -= (r - l + 1) * GetSumu(n / l); // (r - l + 1) 就是 I 在 [l, r] 的和 } return Smu[n] = ret; // 记忆化 }(2) 的前缀和
考虑到 的性质 ,取
即 的前缀和为
杜教筛代码:
inline ll GetSphi(int n) { if(n <= N) return sump[n]; // 提前筛好的 if(Sphi[n]) return Sphi[n]; // 记忆化 ll ret = 1ll * n * (n + 1) / 2; // f * g = id 的前缀和 for(int l = 2, r; l <= n; l = r + 1) { r = n / (n / l); ret -= (r - l + 1) * GetSphi(n / l); // 同上,因为两个的 g 都是 I } return Sphi[n] = ret; // 记忆化 }(1) & (2) 就是杜教筛模板 luogu p4213
(3) (综合)
令 , 考虑迪利克雷卷积的形式得到 $(f*g)(n)=\sum \limits _{d|n} (\varphi(d) \cdot d) \cdot (\frac{n}{d}) = n \sum\limits_{d|n}\varphi(d)=n^2$
即
这样就可以快速求得 的前缀和
就可以了。
题目
先推荐洛谷模板题
还有一题 luoguP3768 简单的数学题,这道题推完式子可以用杜教筛来求 的前缀和,和 所差无几。
51nod 上也有很多杜教筛的题目,放几个:
- 51nod 1244
- 51nod 1237
- 51nod 1238
- 51nod 1239
- 51nod 1220
- ...
- 1
信息
- ID
- 3168
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者