学习笔记:筛法
杜教筛
可以在 \(O(n^{2/3})\) 的时间复杂度内求出某些积性函数的前缀和。
具体地,对于积性函数 \(f\),如果能找到另一个积性函数 \(g\),使得 \(h=f\ast g\) 的前缀和容易计算,那么 \(f\) 就可以杜教筛。
推导
设 \(f\) 的前缀和 \(F(n)=\sum\limits_{1\le i\le n}f(i)\),\(g\) 的前缀和 \(G(n)=\sum\limits_{1\le i\le n}g(i)\),\(h\) 的前缀和 \(H(n)=\sum\limits_{1\le i\le n}h(i)\)。
展开 \(h\) 的前缀和:
因为全程只是用了 \(f,g\) 这两个符号,所以可以直接交换二者,得到:
移项,得到:
发现此时 \(F\) 可以利用数论分块和记搜,递归求解了。这就是杜教筛的理论依据。
如何构造 \(g\)?
暂时不是很会。 似乎很多时候都选取 \(1\) 来作为 \(g\),常用结论 \(\varphi\ast 1=\text{id},\mu\ast 1=[n=1]\);有时候选 \(\mu\),常用结论 \(\text{id}\ast\mu=\varphi,1\ast\mu=[n=1]\);似乎还有时候会选 \(\text{id}\),常用结论 \((n\cdot\varphi(n))\ast\text{id}=\text{id}^2\),也就是构造平方和。
找不到怎么办?使用 min_25 筛。
代码
以 P4213 【模板】杜教筛 为例,求 \(\varphi\) 和 \(\mu\) 的前缀和。
对于 \(\mu\),可以取 \(g(n)=1\),于是有 \(h(n)=(\mu\ast g)(n)=[n=1]\);对于 \(\varphi\),同样取 \(g(n)=1\),于是有 \(h(n)=(\varphi\ast g)=n\)。
namespace Math{
unordered_map<int,int>mem_phi,mem_mu;
int tot;
int pri[N],mu[N],phi[N];
bool np[N];
inline void init(int n){
mu[1]=1,phi[1]=1;
for(int i=2;i<=n;++i){
if(!np[i])phi[i]=i-1,pri[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*pri[j]<=n;++j){
np[i*pri[j]]=1;
if(!(i%pri[j])){phi[i*pri[j]]=phi[i]*pri[j],mu[i*pri[j]]=0;break ;}
phi[i*pri[j]]=phi[i]*(pri[j]-1),mu[i*pri[j]]=-mu[i];
}
}
for(int i=1;i<=n;++i)mem_phi[i]=mem_phi[i-1]+phi[i],mem_mu[i]=mem_mu[i-1]+mu[i];
return ;
}
inline int qry_pre_mu(int n){
if(mem_mu.count(n))return mem_mu[n];
int l=2,r=0;
int res=0;
for(;l<=n;l=r+1){
r=n/(n/l);
res+=qry_pre_mu(n/l)*(r-l+1);
}
res=1-res;
return mem_mu[n]=res;
}
inline int qry_mu(int l,int r){
return qry_pre_mu(r)-qry_pre_mu(l-1);
}
inline int qry_pre_phi(int n){
if(mem_phi.count(n))return mem_phi[n];
int l=1,r=0;
int res=0;
for(;l<=n;l=r+1){
r=n/(n/l);
res+=(n/l)*(n/l+1)/2*qry_mu(l,r);
}
return mem_phi[n]=res;
}
}using namespace Math;
为什么不要求 \(g\) 的前缀和也容易求?
以 P4318 为例,此题可以取 \(g(n)=[\exists k,k^2=n]\),这样构造使得 \(h(n)=1\) 恒成立,进而有 \(H(n)=n\);同时在枚举杜教筛最后那个式子里的 \(d\) 时只需要枚举平方因子并去掉 \(g\),进而规避掉 \(g\) 的前缀和。
min_25 筛
黑科技。咕咕咕。
PN 筛
黑科技。咕咕咕。

浙公网安备 33010602011771号