模板题
前置知识
杜教筛
杜教筛就是在非线性时间内求出积性函数前缀和的一种算法
现在要求(S(n)=sumlimits_{i=1}^nf(i))的值
其中f(i)是积性函数,n很大以至于O(n)并不能通过
考虑一个积性函数(g(x)),求((f imes g)(x))的前缀和
[ herefore sumlimits_{i=1}^n(f imes g)(i)\
=sumlimits_{i=1}^nsumlimits_{d|i}f(frac id)g(d)\
=sumlimits_{d=1}^ng(d)sumlimits_{i=1}^{lfloorfrac nd
floor}f(i)\
=sumlimits_{d=1}^ng(d)S(lfloorfrac nd
floor)\
]
因为g是积性函数,所以g(1)=1,所以S(n)=g(1)S(n)
[ herefore S(n)=g(1)S(n)\
=sumlimits_{i=1}^ng(i)S(lfloorfrac ni
floor)-sumlimits_{i=2}^ng(i)S(lfloorfrac ni
floor)\
=sumlimits_{i=1}^n(f imes g)(i)-sumlimits_{i=1}^ng(i)S(lfloorfrac ni
floor)
]
其中(sumlimits_{i=1}^n(f imes g)(i))当(f)为(mu),(varphi),(varphi imes id)时,取(g)为(I),则可以O(1)求出其值
分别为(1),(frac{n(n+1)}{2})和(frac{n(n+1)(2n+1)}6)
其中(sumlimits_{i=1}^ng(i)S(lfloorfrac ni floor))可以用数论分块求出
总时间复杂度(O(n^{frac 34}))
如果先预处理出(S(1)到S(m)),则时间复杂度可以优化到(O(frac n{sqrt m})),证明略
Code
#include<bits/stdc++.h>
#define N 5000000
#define ll long long
using namespace std;
ll mu[N+varphiphi[N+5],prim[N+5],tot;
bool apr[N+5];
void pre(int n){
mu[varphiphi[1]=1;
for(int i=2;i<=n;i++){
if(!apr[i])prim[++tot]=i,mu[i]=varphiphi[i]=i-1;
for(int j=1;j<=tot&&prim[j]*i<=n;j++){
apr[i*prim[j]]=1;
if(i%prim[j]){
mu[i*prim[j]]=-mu[i];
varphiphi[i*prim[jvarphiphi[varphiphi[prim[j]];
}else{
mu[i*prim[j]]=0;
varphiphi[i*prim[jvarphiphi[i]*prim[j];
break;
}
}
}
for(int i=2;i<=n;i++)mu[i]+=mu[i-varphiphi[ivarphiphi[i-1];
}
map<int,ll>mu_;
ll SumMu(int n){
if(n<=N)return mu[n];
if(mu_[n])return mu_[n];
ll re=0;
for(int l=2,r=0;r<0x7fffffff&&l<=n;l=r+1){
r=n/(n/l);
re+=1ll*(r-l+1)*SumMu(n/l);
}
return mu_[n]=1ll-re;
}
map<int,varphiphi_;
ll varphiPhi(int n){
if(n<=N)retuvarphiphi[n];
varphiphi_[n])retuvarphiphi_[n];
ll re=0;
for(int l=2,r=0;r<0x7fffffff&&l<=n;l=r+1){
r=n/(n/l);
re+=1ll*(r-l+1)*varphiPhi(n/l);
}
retuvarphiphi_[n]=1ll*n*(n+1ll)/2ll-re;
}
signed main(){
pre(N);
int t,n;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
printf("%lld %lld
",varphiPhi(n),SumMu(n));
}
return 0;
}
/*
6
1
2
8
13
30
2333
*/