杜教筛学习笔记

计算 (S(n)=sum_{i=1}^n f(i))

对于一个积性函数 (g),有(第二行是将 (i,j) 分别枚举 (d,frac{n}{d})

[egin{aligned}sum_{i=1}^n (f*g)(i) &=sum_{i=1}^nsum_{d|i} f(d)g(frac{i}{d})\ &=sum_{i=1}^nsum_{j=1}^{lfloorfrac{n}{i} floor}g(i)f(j)\ &=sum_{i=1}^ng(i)Sleft(lfloorfrac{n}{i} floor ight) end{aligned} ]

然后把这个求和式的第一项拿出来,就有了:

[g(1)S(n)=sum_{i=1}^n (f*g)(i)-sum_{i=2}^ng(i)Sleft(lfloorfrac{n}{i} floor ight) ]

这个就是杜教筛的主要套路了(

一般对这个 (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)

原文地址:https://www.cnblogs.com/suxxsfe/p/14555975.html