[学习笔记] 杜教筛
好久以前就写过了,但是现在才搞懂原理。
前置知识
首先称一个函数 \(f(x)\) 为 积性函数 ,当且仅当对于任意两个互质的数 \(a,b\) ,有:
更特殊地,称一个函数 \(f(x)\) 为 完全积性函数 ,当且仅当对于任意 \(a,b\) ,有:
常见的积性函数有 \(\mu,\varphi,\sigma,d\) ,完全积性函数有:\(\epsilon,I,id\)
\(\epsilon(n)=[n=1],I(n)=1,id(n)=n\)
对于两个函数 \(f,g\) ,定义他们的 迪利克雷卷积 为:
对于上面我们已经给出函数,有这样一些卷积关系是:
补充一个东西,\(\sum_{x|n}\varphi(x)=n\) 的证明,考虑两个互质的数 \(m,n\) ,有:
这说明了 \(f(x)=\sum_{x|n}\varphi(x)\) 是一个积性函数,对于质数有 \(f(p_i^{k_i})=p_i^{k_i}\) ,那么 \(f(n)=n\)
杜教筛
他是用来解决这样一个问题:$$\sum_{i=1}^nf(i)=S(n)$$ ,做法是找一个 辅助函数 \(g\) ,也就是我们先求出 \(f*g\) 的前缀和,再算 \(f\) 的前缀和,选取 \(g\) 的标准就是 \(f*g\) 和 \(g\) 的前缀和都是很好算的 ,先来推柿子:
然后套路地枚举 \(d\) (这种东西莫比乌斯反演的时候都见惯了):
考虑 用前缀和来表示单个项 \(g(1)S(n)\) ,因为前缀和是好算的:
假定前面的项是好算的,后面的项可以用数论分块来解决,容易发现我们得到了一个递归的问题。
模板题
\(\mu\) 的前缀和
考虑到莫比乌斯反演的那个柿子:\(\mu*I=\epsilon\) ,令 \(f=\mu,g=I,f*g=\epsilon\)
ll get_mu(int n)
{
if(n<=N) return mu[n];//这里是预处理的mu
if(smu[n]) return smu[n];//这里是map记忆化
ll ans=1;//f*g的前缀和
for(int l=2,r;r<inf && l<=n;l=r+1)//数论分块
{//这道题要卡 inf,因为可能加爆
r=n/(n/l);
ans-=(r-l+1)*get_mu(n/l);//I的求和是r-l+1,然后递归
}
return smu[n]=ans;//得到答案
}
\(\varphi\) 的前缀和
考虑到欧拉反演的柿子:\(\varphi*I=id\) ,令 \(f=\varphi,g=I,f*g=id\)
\(id\) 的前缀和特别好算,就是:\(\frac{n(n+1)}{2}\)
ull get_ph(int n)
{
if(n<=N) return phi[n];//这里是预处理的phi
if(sph[n]) return sph[n];//map记忆化
ull ans=(ull)n*(n+1ll)/2;//要开ull
for(int l=2,r;r<inf && l<=n;l=r+1)//数论分块
{
r=n/(n/l);
ans-=(r-l+1)*get_ph(n/l);//I的求和是r-l+1,然后递归
}
return sph[n]=ans;//得到答案
}
扩展:\(\varphi\cdot id\) 的前缀和
需要说明一下:\(\varphi\cdot id=\sum_{i=1}^n\varphi(i)i\)
令 \(f=\varphi\cdot id\) ,\(g=id\) ,\(f*g=(\varphi\cdot i)*i\) ,我们来看这个东西的前缀和好不好算:
最后一步是欧拉反演,所以他的前缀和是 \(\frac{n(n+1)(2n+1)}{6}\)
现在做一个小总结吧,杜教筛的关键是找辅助函数,让前缀和变得好算。
其他例题
这两个是一类题,都是函数结构递归,然后杜教筛处理边界:循环之美,DZY Loves Math IV