[BZOJ3944]Sum(杜教筛)
3944: Sum
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 6201 Solved: 1606
[Submit][Status][Discuss]Description
Input
一共T+1行第1行为数据组数T(T<=10)第2~T+1行每行一个非负整数N,代表一组询问
Output
一共T行,每行两个用空格分隔的数ans1,ans2
Sample Input
6
1
2
8
13
30
2333Sample Output
1 1
2 0
22 -2
58 -3
278 -3
1655470 2
HINT
Source
[Submit][Status][Discuss]
最基础的杜教筛。
杜教筛实际上就是这样一个式子:$$F(n)=H(n)-\sum\limits_{i=2}^{n}g(i)F(\lfloor\frac{n}{i}\rfloor)$$
设要求的是$f$的前缀和,辅助函数分别是$g$和$h$,$F$,$G$,$H$分别是三个函数的前缀和,如果能在$O(1)$的时间内求出$G$和$H$,就能在$O(n^{\frac{3}{4}})$内求出$F$。复杂度$O(\sum\limits_{i=1}^{\sqrt{n}} \sqrt{\frac{n}{i}})=O(n^\frac{4}{3})$,通过预处理前$n^{\frac{2}{3}}$个数就可以做到$O(n^{\frac{2}{3}})$了。
对于后面的$F(n)$值数组下标不可能直接记录,但是注意到我们最终需要的$F$函数值最多有$O(n^{\frac{2}{3}})$个(因为$\lfloor \frac{\lfloor\frac{a}{b}\rfloor}{c} \rfloor=\lfloor \frac{a}{bc} \rfloor$),所以对于后面的值可以把$x$存到$n/x$里。
回到这题,不要爆int就好了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=l; i<=r; i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=2000010,M=100010; 9 int T,n,m,tot,p[N]; 10 ll phi[N],mu[N],Phi[M],Mu[M]; 11 bool vis[M]; 12 13 ll getphi(int x){ if (x<=m) return phi[x]; else return Phi[n/x]; } 14 ll getmu(int x){ if (x<=m) return mu[x]; else return Mu[n/x]; } 15 16 void solve(int x){ 17 if (x<=m) return; 18 int t=n/x,lst=1; ll p1=0,p2=0; 19 if (vis[t]) return; 20 vis[t]=1; Phi[t]=(1ll*x+1)*x>>1; Mu[t]=1; 21 while (lst<x){ 22 int i=lst+1; lst=x/(x/i); solve(x/i); 23 p1+=getphi(x/i)*(lst-i+1); p2+=getmu(x/i)*(lst-i+1); 24 } 25 Phi[t]-=p1; Mu[t]-=p2; 26 } 27 28 int main(){ 29 freopen("bzoj3944.in","r",stdin); 30 freopen("bzoj3944.out","w",stdout); 31 scanf("%d",&T); m=2000000; phi[1]=mu[1]=1; 32 rep(i,2,m){ 33 if (!phi[i]) p[++tot]=i,phi[i]=i-1,mu[i]=-1; 34 for (int j=1; j<=tot && i*p[j]<=m; j++) 35 if (i%p[j]==0) { phi[i*p[j]]=p[j]*phi[i]; mu[i*p[j]]=0; break; } 36 else phi[i*p[j]]=(p[j]-1)*phi[i],mu[i*p[j]]=-mu[i]; 37 } 38 rep(i,2,m) phi[i]=phi[i-1]+phi[i],mu[i]=mu[i-1]+mu[i]; 39 while (T--){ 40 scanf("%d",&n); memset(vis,0,sizeof(vis)); 41 if (n<=m) printf("%lld %lld\n",phi[n],mu[n]); 42 else solve(n),printf("%lld %lld\n",Phi[1],Mu[1]); 43 } 44 return 0; 45 }