杜教筛(极简版)
迪利克雷卷积
定义*为两个函数的迪利克雷卷积操作。
则
\(h=f*g <=> h(n)=\sum_{d|n} f(d)g(n/d)\)
迪利克雷卷积满足交换律,结合律,分配律
积性函数
若\(gcd(n,m)==1,f(x)\)为积性函数,则\(f(n*m)=f(n)f(m);\)
积性函数的乘积仍为积性函数
任何积性函数都可以线性求出。求时需记录有关最小质因子的一些信息,如\(d(i)\)表示\(i\)的约数个数,就需要一个数组来记录最小质因子的幂次。这个要具体问题具体对待
杜教筛
用非线性的时间来求一个积性函数的前缀和,网传当线性取得前缀和为\(n^{\frac{2}{3}}时\),再配合整除分块,杜教筛的复杂度可以达到\(O(n^{\frac{2}{3}})\)
假设要求积性函数\(f(n)\)的前缀和,即\(\sum_{i=1}^nf(i)\),我们需要找到另一个积性函数\(g(n)\),使得\(h=f*g\),则
\(h(n)=\sum_{d|n} f(\frac{n}{d})g(d)\)
同时求和得
\(\sum_{i=1}^nh(i)=\sum_{i=1}^n\sum_{d|i}f(\frac{i}{d})g(d)=\sum_{d=1}^ng(d)*\sum_{i=1}^{\frac{n}{d}}f(i)\)
令\(S(n)=\sum_{i=1}^nf(i)\),则
\(\sum_{i=1}^nh(i)=g(1)S(n)+\sum_{d=2}^{n}g(d)*S(\lfloor \frac{n}{d} \rfloor)\),移项即得
\(g(1)S(n)=\sum_{1}^nh(i)-\sum_{d=2}^{n}g(d)*S(\lfloor \frac{n}{d} \rfloor)\)
这个算法要求最后\(h(i),g(d)\)的前缀和是好求的,这样就可以用整除分块较快速的求出\(S(n)\)。若要应用杜教筛,这就要求我们记住一些常见的迪利克雷卷积。
例:
1:\(\mu*I=\varepsilon\)
2:\(\phi*I=id\)
3:\(\mu*id=\phi\)
代码实现,题目链接P4213 【模板】杜教筛(Sum)
ll T;
ll n;
int p[maxn],cnt,mu[maxn];
ll phi[maxn];
bool vis[maxn];
struct node{ll s1,s2;};
map<int,node>mp;
void pre(){
mu[1]=phi[1]=1;
rep(i,2,N){
if(!vis[i]){
p[++cnt]=i;
mu[i]=-1;phi[i]=i-1;
}
rep(j,1,cnt){
if(1LL*p[j]*i>N) break;
vis[1LL*p[j]*i]=1;
if(i%p[j]){
mu[i*p[j]]=-mu[i];
phi[i*p[j]]=phi[i]*phi[p[j]];
}
else{
phi[i*p[j]]=phi[i]*p[j];
break;
}
}
}
rep(i,1,N) mu[i]+=mu[i-1],phi[i]+=phi[i-1];
}
node solve(ll x){
node tmp;
if(x<=N){tmp.s1=phi[x];tmp.s2=mu[x];return tmp;}
if(mp.find(x)!=mp.end()){return mp[x];}
tmp.s1=1LL*x*(x+1)/2;tmp.s2=1;
node tep;
for(ll l=2,r;l<=x;l=r+1){
r=x/(x/l);
tep=solve(x/l);
tmp.s1-=1LL*(r-l+1)*tep.s1;
tmp.s2-=1LL*(r-l+1)*tep.s2;
}
return mp[x]=tmp;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in1.txt","r",stdin);
// freopen("2.out","w",stdout);
#endif
pre();
T=read();node ans;
while(T--){
n=read();
ans=solve(n);
print(ans.s1);ptc;
print(ans.s2);pts;
}
return 0;
}
这里\(map\)的使用可以很大程度的减少时间复杂度,如果手写\(hash\)表的话,速度将更快

浙公网安备 33010602011771号