杜教筛是要记忆化的。但是$map$的常数非常大,亲测$Hash Table$也很难跑过现在luogu上的杜教筛模板。怎么办呢?如果我们能够预处理出所有会被用到的数,给他们分配好空间,那么不仅可以避开$map$的常数,还可以从小到大依次枚举这些数进行处理,从而避免递归。
观察$S(n)=h(n)-\sum_{i=2}^n S(\lfloor{n\over i}\rfloor)$,可知会被取到的数可以分为两类:
1.小于$\sqrt n$的所有自然数
2.一个大于$\sqrt n$的数$x$,并且存在一个小于$\sqrt n$的数,使$\lfloor {n\over i}\rfloor = x$成立。
由此也可以看出来,可能被取的数只有$2\cdot \sqrt n$个。
那么可以一次性处理出来所有要计算的$n$。从小到大枚举$i$,当$\lfloor {n\over i}\rfloor$大于$2e6$(就是最初线筛出来的一部分前缀和,取$n^{2\over 3}$时最优秀)退出。
还有一个问题,我们在进行杜教筛的时候,怎么知道$n$在数组中对应的位置呢?
有一个结论,是$[1,\sqrt n]$中的每一个数$i$,$\lfloor{n\over i}\rfloor$唯一地映射到了一个大于$\sqrt n$的数;每一个大于$\sqrt n$、并且可以被$\lfloor{n\over i}\rfloor$映射到的数$x$,$\lfloor {n\over x} \rfloor $会映射到$i$,并且对于所有的会被映射到的数$x$,只有一个数的$\lfloor {n\over x}\rfloor$会等于$i$。
因此,从小到大枚举$i$,并且按照$i$的大小顺序将$\lfloor {n\over i}\rfloor$存入数组,那么对于大于$\sqrt n$的$x$数组中的第$n\over x$个位置就是这个数所在的位置。
然后就可以愉快地杜教筛啦~~
下面是luogu杜教筛模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #define ll long long using namespace std; template <class T> inline void read(T &x) { x=0; char c=getchar(); int f=1; while(!isdigit(c)){if(c=='-')f=-1; c=getchar();} while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f; }
const int N=2e6+10; int flg[N],pri[N],num,m; ll mu[N],phi[N]; void predo() { m=2e6; phi[1]=1,mu[1]=1; for(int i=2;i<=m;++i) { if(!flg[i]) pri[num++]=i,phi[i]=i-1,mu[i]=-1; for(int j=0;j<num&&pri[j]*i<=m;++j) { flg[i*pri[j]]=1; if(i%pri[j]) mu[i*pri[j]]=-mu[i],phi[i*pri[j]]=phi[i]*(pri[j]-1); else{ phi[i*pri[j]]=phi[i]*pri[j]; break;} } } for(int i=1;i<=m;++i) phi[i]+=phi[i-1],mu[i]+=mu[i-1]; } ll Mu[N],Phi[N]; int v[N],top,n; inline ll qphi(int x){return x<=m?phi[x]:Phi[n/x];} inline ll qmu(int x){return x<=m?mu[x]:Mu[n/x];} int main() { int T; read(T); predo(); while(T--) { read(n); top=0; for(int i=1;i<=n;++i) { if((n/i)<=m) break; v[++top]=n/i; } for(int i=top;i;--i) { int t=v[i]; ll ap=t*(ll)(t+1)/2,am=1; for(int l=2,r=1;r<t;l=r+1) r=t/(t/l),ap-=qphi(t/l)*(r-l+1),am-=qmu(t/l)*(r-l+1); Phi[i]=ap,Mu[i]=am; } printf("%lld %lld\n",qphi(n),qmu(n)); } return 0; }
|