杜教筛是圈内的一个老师发明的,有狄利克雷卷积推出来的一个算f(n)的前缀和的比较快的算法大概在O(2/3)范围内
通常令g=1算一些特殊函数的前缀和:
莫比乌斯函数
cosnt int maxn=1e5+9;
vector<int>primes;
bool vis<maxn>;
ll mu[maxn],smu[maxn];
unordered_map<ll ,ll >memu;
void init(){
mu[1]=1;
for(int i=2;i<maxn;i++){
if(!vis[i])primes.push_back(i),mu[i]=-1;
for(auto p: primes){
if(p*i>maxn)break;
vis[i*p]=1;
if(i%p==0)[i*p]=0;
else mu[i*p]=mu[i]*mu[p];
}
}
for(int i=1;i<maxn;i++){
smu[i]=smu[i-1]+mu[i];
}
}
int s_mu(int n){
if(n<maxn)return smu[n];
if(memu.count(n))return memu[n];
ll ans=1;
for(int l=2,r;l<=maxn;l=r+1){
r=n/(n/l);
ans-=(r-l+1)*(s_mu(n/l));
}
memu[n]=ans;
return ans;
}
欧拉函数
void phiinit(){
phi[1]=1;
for(int i=2;i<maxn;i++){
if(!vis[i])primes.push_back(i),phi[i]=i-1;
for(auto p: primes){
if(p*i>maxn)break;
vis[i*p]=1;
phi[i*p]=phi[i]*phi[p];
if(i%p==0)phi[i*p]=i*phi[p];
}
}
for(int i=1;i<maxn;i++){
sphi[i]=sphi[i-1]+phi[i];
}
}
int s_phi(int n){
if(n<maxn)return smu[n];
if(memu.count(n))return mephi[n];
ll ans=n*(n+1)/2;
for(int l=2,r;l<=maxn;l=r+1){
r=n/(n/l);
ans-=(r-l+1)*(s_phi(n/l));
}
mephi[n]=ans;
return ans;
}
这里我先对函数进行了线性筛法,然后保存前缀和,对大于maxn的数用杜教筛计算,这里算后半部分的过程是利用了整体分快的思想,加大时间效率同时用哈希map对去过的数进行一个记忆化。