传送门php
求\(\sum_{i=1}^{n}\mu(i)\)和\(\sum_{i=1}^{n}\varphi(i)\)c++
数据范围线性不可作。jsp
须要使用杜教筛。函数
杜教筛能够在非线性时间里求出一个积性函数的前缀和。spa
借这里先写一些杜教筛内容。。。或许之后会补总结(雾code
最开始扔积性函数:递归
先放狄利克雷卷积的式子:get
假设咱们如今有两个数论函数\(f,g\),则这两个函数的卷积是\((f*g)(n)=\sum_{d\mid n}f(d)·g(\frac{n}{d})\)后面的括号表示范围,通常不写的时候能够默认其为\(n\)。it
能够推出狄利克雷卷积知足如下运算律模板
能够类比乘法运算律记忆。
那么咱们能够开始搞杜教筛了。
如今咱们要求一个积性函数\(f\)的前缀和,也就是\(\sum_{i=1}^{n}f(i)\)。
咱们尝试构造两个积性函数使\(h=f*g\)
那么咱们求一下\(\sum_{i=1}^{n}h(i)\)。
先记\(Sum(n)\)为\(\sum_{i=1}^{n}f(i)\)
则:
\[ \sum_{i=1}^{n}h(i)=\sum_{i=1}^{n}\sum_{d\mid i}g(d)f(\frac{i}{d}) \]
而后明显能够反过来枚举。
\[ →\sum_{d=1}^{n}g(d)\sum_{d\mid i}f(\frac{i}{d}) \]
改为枚举\(\frac{i}{d}\)
\[ →\sum_{d=1}^{n}g(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}f(i)=\sum_{d=1}^{n}g(d)S(\lfloor\frac{n}{d}\rfloor) \]
而后把式子的第一项提出来,整个代回去。
\[ \sum_{i=1}^{n}h(i)=g(1)·S(n)+\sum_{d=2}^{n}g(d)·S(\lfloor\frac{n}{d}\rfloor) \]
移项
\[ g(1)·S(n)=\sum_{i=1}^{n}h(i)-\sum_{d=2}^{n}g(d)·S(\lfloor\frac{n}{d}\rfloor) \]
这样\(g(1)\)明显为\(1\),因此这个式子就很明显了,只要\(h(i)\)的前缀和好求那么这个式子就能够在非线性时间里求出来了。
由于\(h=f*g\)咱们换个形式表示上面的式子。
\[ →g(1)·S(n)=\sum_{i=1}^{n}(f*g)(i)-\sum_{d=2}^{n}g(d)·S(\lfloor\frac{n}{d}\rfloor) \]
因此只要找到一个合适的\(g\)就好了。
看个例子,咱们这个题要求啥来着,\(\sum_{i=1}^{n}\mu(i)\)和\(\sum_{i=1}^{n}\varphi(i)\)
先看第一个。
就不推了,根据上面那个把\(f\)换成\(\mu\)直接代到最后面。
那么应该怎么给\(g\)取值呢,咱们能够简明扼要的先看一下那一项变成什么了。
\[ →\sum_{i=1}^{n}(\mu*g)(i) \]
有一个好消息,咱们知道\(\mu*I=\epsilon\)。那么能够把上面的式子当作
\[ \sum_{i=1}^{n}(\mu*I)(i)=\sum_{i=1}^{n}\epsilon(i) \]
元函数的前缀和就很是好求,就是\(1\),因此咱们求的答案
\[ S(n)=1-\sum_{d=2}^{n}g(d)·S(\lfloor\frac{n}{d}\rfloor) \]
再看第二个,咱们仍是相同的直接把\(\varphi\)代到最后面去。
则咱们有式子
\[ \sum_{i=1}^{n}(\varphi*g)(i) \]
思考一下,咱们记得欧拉函数有个有趣的性质\(\sum_{d|n}\varphi(d)=n\)
咱们把它用卷积的形式表达,就是\(\varphi*I=id\)
带入刚才的式子里面。
\[ \sum_{i=1}^{n}(\varphi*g)(i)=\sum_{i=1}^{n}id(i) \]
明显的小高斯5岁就会的那个数列求和。。。
这个东西是\(n·(n+1)/2\)应该都知道。。。
而后代码实现的时候,能够先筛出根号范围内的答案,而后递归处理记忆化搜索。
因为须要储存下标很是大的值,因此须要使用哈希或者偷懒使用unordered_map,不要用map,会多一个log。
下面代码实测BZOJ可过,注意少开long long
#include <bits/stdc++.h> #include <tr1/unordered_map> using namespace std; const int MAXN=4e6+7; const int M=4e6; #define ll long long bool vis[MAXN]; int mu[MAXN],sum1[MAXN]; ll phi[MAXN],sum2[MAXN]; int cnt,prime[MAXN]; tr1::unordered_map<ll,ll> w1; tr1::unordered_map<int,short> w; inline void get(int N) { phi[1]=mu[1]=1; for(int i=2;i<=N;i++){ if(!vis[i]){ prime[++prime[0]]=i; mu[i]=-1; phi[i]=i-1; } for(int j=1;j<=prime[0];j++){ if(i*prime[j]>N) break; vis[i*prime[j]]=1; if(i%prime[j]==0){ phi[i*prime[j]]=phi[i]*prime[j]; break; } else mu[i*prime[j]]=-mu[i],phi[i*prime[j]]=phi[i]*(prime[j]-1); } } for(int i=1;i<=N;i++) sum1[i]=sum1[i-1]+mu[i],sum2[i]=sum2[i-1]+phi[i]; } int djsmu(int x) { if(x<=M) return sum1[x]; if(w[x]) return w[x]; int ans=1; for(int l=2,r;l<=x;l=r+1){ if(r==2147483647) break; r=x/(x/l); ans-=(r-l+1)*djsmu(x/l); } return w[x]=ans; } ll djsphi(int x) { if(x<=M) return sum2[x]; if(w1[x]) return w1[x]; ll ans=1ll*x*(1ll*x+1)/2; for(int l=2,r;l<=x&&l>=0;l=r+1){ if(r==2147483647) break; r=x/(x/l); ans-=1ll*(r-l+1)*djsphi(x/l); } return w1[x]=ans; } inline int read() { int x=0,c=1; char ch=' '; while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); while(ch=='-')c*=-1,ch=getchar(); while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); return x*c; } int main() { int T=read(); get(M); while(T--){ int n; n=read(); printf("%lld %d\n", djsphi(n),djsmu(n)); } }