计算 (S(n)=sum_{i=1}^n f(i))
对于一个积性函数 (g),有(第二行是将 (i,j) 分别枚举 (d,frac{n}{d}):
然后把这个求和式的第一项拿出来,就有了:
这个就是杜教筛的主要套路了(
一般对这个 (Sleft(lfloorfrac{n}{i} floor ight)) 整除分块来递归地求解
至于复杂度,线性筛预处理前 (n^{frac{2}{3}}) 然后剩下的再递归求的复杂度是 (O(n^{frac{2}{3}}))
多测的时候还可以开个 map 把已经求过的值存下来
一个模板题:https://www.luogu.com.cn/problem/P4213
求 (varphi)
此时肯定要令 (f=varphi),然后问题是如何找到一个合适的 (g)
由于想到 (varphi * operatorname{1}=operatorname{Id}),所以可以令 (g=operatorname{1})
此时可以发现,(g(i)) 和 (sum_{i=1}^n (f*g)(i)) 都很好求,于是可以直接杜教筛
inline long long get_phi(int n){
if(n<=maxn) return phi[n];
if(ans_phi.find(n)!=ans_phi.end()) return ans_phi[n];
long long ans=((long long)n*(n+1ll))>>1;
for(reg unsigned int l=2,r;l<=n;l=r+1){
r=n/(n/l);
ans-=(r-l+1)*get_phi(n/l);
}
return ans_phi[n]=ans;
}
求 (mu)
令 (f=mu,g=operatorname{1}),于是就有 (f*g=epsilon)
那就做完了
inline int get_mu(int n){
if(n<=maxn) return mu[n];
if(ans_mu.find(n)!=ans_mu.end()) return ans_mu[n];
long long ans=1;
for(reg unsigned int l=2,r;l<=n;l=r+1){
r=n/(n/l);
ans-=(r-l+1)*get_mu(n/l);
}
return ans_mu[n]=ans;
}
总而言之就是想办法找 (g)
像 P3768 简单的数学题 推完式子有一部分是 (varphi(k)k^2) 的前缀和,于是就可以构造 (f=operatorname{Id}^2cdot varphi,g=operatorname{Id}^2),然后就能发现 (sum_{i=1}^n (f*g)(i)=n^3) 了
(epsilon=mu * 1)
(d=1 * 1)
(sigma=operatorname{id} * 1)
(varphi=mu * operatorname{id})
(operatorname{id}=varphi * 1)