杜教筛 模板
转自:
1. 求$\sum\limits_{i=1}^n\mu(i),n\leqslant 10^{11}$
直接求不好求,但是我们有$\sum\limits_{i=1}^n\sum\limits_{d|i}\mu(d)=1$
化一下蛤:$\sum\limits_{i=1}^n\sum\limits_{j=1}^{\left\lfloor\frac{n}{i}\right\rfloor}\mu(j)=1$,$\sum\limits_{i=1}^n\mu(i)=1-\sum\limits_{i=2}^n\sum\limits_{j=1}^{\left\lfloor\frac{n}{i}\right\rfloor}\mu(j)$
核心思想是枚举约数,这样就可以递推/递归求解了。
时间复杂度$O\left(n^{\frac34}\right)$。
但是注意到当$n$比较小的时候其实我们可以$O(n)$线筛出来。
所以我们考虑分类讨论,线筛出≤B的,>B的递推。
时间复杂度$O\left(B+\sum\limits_{i=1}^{\frac nB}\sqrt{\frac ni}\right)=O\left(B+\frac n{\sqrt{B}}\right)$
求导一下可知在$B=n^{\frac23}$时取得最小值$O\left(n^\frac23\right)$。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<map> 6 #define ll long long 7 #define N 5000005 8 using namespace std; 9 const int inf = 5000000; 10 map<int,ll>mp; 11 int su[N],pr[N],tot; 12 ll f[N]; 13 void shai() 14 { 15 f[1]=1; 16 for(int i=2;i<=inf;i++) 17 { 18 if(!pr[i]) 19 { 20 pr[i]=i; 21 su[++tot]=i; 22 f[i]=i-1; 23 } 24 for(int j=1;j<=tot&&su[j]<=pr[i]&&su[j]*i<=inf;j++) 25 { 26 pr[su[j]*i]=su[j]; 27 if(su[j]==pr[i])f[su[j]*i]=f[i]*su[j]; 28 else f[su[j]*i]=f[i]*(su[j]-1); 29 } 30 } 31 for(int i=1;i<=inf;i++) 32 { 33 f[i]=f[i-1]+f[i]; 34 } 35 return ; 36 } 37 int n; 38 ll phi(int x) 39 { 40 if(x<=inf)return f[x]; 41 if(mp.find(x)!=mp.end())return mp[x]; 42 ll as=1LL*x*(x+1)/2;int r; 43 for(int l=2;l<=x;l=r+1) 44 { 45 r=x/(x/l); 46 as-=phi(x/l)*(r-l+1); 47 } 48 return mp[x]=as; 49 } 50 int main() 51 { 52 shai(); 53 scanf("%d",&n); 54 printf("%lld\n",phi(n)); 55 return 0; 56 } 57