傳送門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)); } }