【Bzoj3944】杜教筛模板(狄利克雷卷积搞杜教筛)
哇杜教筛超炫的
有没有见过$O(n^\frac{2}{3})$求欧拉函数前缀和的算法?没有吧?蛤蛤蛤
首先我们来看狄利克雷卷积是什么
首先我们把定义域是整数,陪域是复数的函数叫做数论函数。
然后狄利克雷卷积是个函数和函数的运算。
比如说有两个数论函数f,g
那么它们的狄利克雷卷积就是f*g,记为h
然后我们惊奇地发现$h(i)=\sum\limits_{d|i}f(d)g(\frac{i}{d})$
而且狄利克雷卷积好像是个群,然后它就能满足交换律结合律分配律balaba
那么这个玩意有什么卵用呢?
(显然它很有卵用)
我们再说一个数论函数叫单位元。
e(1)=1 e(n)=0(n>1)
然后我们发现任意函数f(x)有f*e=f
然后就引出我们的杜教筛,这里计算莫比乌斯函数的前缀和
设$S(n)=\sum\limits_{i=1}^{n}miu(i)$
然后我们随便代一个数论函数g,套上狄利克雷卷积
$\sum\limits_{i=1}^{n}(g*miu)(i)=\sum\limits_{i=1}^{n}\sum\limits_{d|i}miu(\frac{i}{d})g(d)$
$=\sum\limits_{d=1}^{n}\sum\limits_{d|i}miu(\frac{i}{d})g(d)$
$=\sum\limits_{d=1}^{n}g(d)\sum\limits_{i=1}^{\frac{n}{d}}miu(i)$
$=\sum\limits_{d=1}^{n}g(d)S(\frac{n}{d})$
所以说$g(1)S(n)=\sum\limits_{i=1}^{n}g(i)S(\frac{n}{i})-\sum\limits_{i=2}^{n}g(i)S(\frac{n}{i})$
$=\sum\limits_{i=1}^{n}(g*miu)(i)-\sum\limits_{i=2}^{n}g(i)S(\frac{n}{i})$
然后我们惊奇的发现,如果我们设g(x)=1
因为1*miu=e(因为有个定理是$\sum\limits_{d|n}miu(d)=$ n=1时1,n>1时0)
那么这玩意就变成了$S(n)=1-\sum\limits_{i=2}^{n}S(\frac{n}{d})$
然后这个玩意可以先用线性筛求出$n^\frac{2}{3}$的前缀和,然后用这个表达式应用数论分块,递归搞搞就好了
就问炫不炫
#include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #include<cctype> #include<map> #define maxn 5000000 using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } int prime[maxn],tot; bool s[maxn]; long long phi[maxn]; long long miu[maxn]; map<int,long long>_phi,_miu; long long calcmiu(long long n){ if(n<maxn) return miu[n]; if(_miu.count(n)) return _miu[n]; long long x=2,ans=1; while(x<=n){ long long y=n/(n/x); ans-=calcmiu(n/x)*(y-x+1); x=y+1; } return _miu[n]=ans; } long long calcphi(long long n){ if(n<maxn) return phi[n]; if(_phi.count(n)) return _phi[n]; long long x=2,ans=n*(n+1)/2; while(x<=n){ long long y=n/(n/x); ans-=calcphi(n/x)*(y-x+1); x=y+1; } return _phi[n]=ans; } int main(){ int T=read(); s[1]=1;miu[1]=1;phi[1]=1;miu[0]=0;phi[0]=0; for(int i=2;i<maxn;++i){ if(!s[i]){ s[i]=1; phi[i]=i-1; miu[i]=-1; prime[++tot]=i; } for(int j=1;j<=tot&&prime[j]*i<maxn;++j){ s[prime[j]*i]=1; if(i%prime[j]){ miu[i*prime[j]]=-miu[i]; phi[i*prime[j]]=phi[i]*(prime[j]-1); } else{ phi[i*prime[j]]=phi[i]*prime[j]; miu[i*prime[j]]=0; break; } } } for(int i=2;i<maxn;++i){ phi[i]+=phi[i-1]; miu[i]+=miu[i-1]; } while(T--){ long long n=read(); printf("%lld %lld\n",calcphi(n),calcmiu(n)); } return 0; }