杜教筛
快速求一个积性函数 \(f\) 的前缀和,
记为 \(S\)
\[S(n)=\sum_{i=1}^{n} f(i)
\]
考虑一个积性函数 \(g\)
\[\begin{aligned}
& \sum_{i=1}^{n}(f*g)(i) \\
=& \sum_{i=1}^{n}\sum_{d|n}^{i}f(d)g(\frac{i}{n}) \\
=& \sum_{d=1}^ng(d)\sum_{i=1}^{\frac{n}{d}}f(i) \\
=& \sum_{d=1}^{n}g(d)S(\frac{n}{d}) \\
=& \sum_{d=2}^ng(d)S(\frac{n}{d})+g(1)\times S(n)
\end{aligned}
\]
所以我们得到了最重要的式子:
\[g(1)S(n)=\sum_{i=1}^{n}(f*g)(i)-\sum_{d=2}^ng(d)S(\frac{n}{d})
\]
所以我们只要构造出一个 \(g\) ,我们能快速求出它的前缀和以及 \(f * g\) 的前缀和,那么对后面的整数分块, 复杂度 \(O(n^{\frac{3}{4}})\) , 如果预处理到 \(5e6\), 复杂度近似为 \(O(n^{\frac{2}{3}})\)。
伪代码:
ll GetSum(int n) { // 算 f 前缀和的函数
记忆化
ll ans = sum( (f * g)(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 Sum(n) = ans;//记忆化
}
常用公式:
\[\mu\ *\ 1 = \epsilon
\]
\[\varphi\ *\ 1=Id
\]
一个小小的拓展:
求 \(\sum_{i=1}^n\varphi(i) \times i\)
首先来了解一个结论:
\[(\varphi \times Id)\ *\ Id=n^2
\]
(手推一下)
这题就是求 \(f=\varphi\times Id\) 的前缀和,
令 \(g=Id\)
\[g(1)S(n)=\sum_{i=1}^n(f*g)(i)-\sum_{d=2}^ng\times S(\frac{n}{d})
\]
\[(f*g)(n)=n^2
\]
\((f*g)(n)\) 的前缀和为 \(\frac{n(n+1)(2n+1)}{6}\).
然后就可以筛了。