BZOJ-3944 Sum(杜教篩模板)
題目描述
求 \(S_1(n)=\displaystyle\sum_{i=1}^{n}\varphi(i)\) 和 \(S_2(n)=\displaystyle\sum\limits_{i=1}^{n}\mu(i)\) 的值,\(T\leq 10,n<2^{31}-1\)。
杜教篩
在莫比烏斯反演的題目中,往往要求出一些數論函式的字首和,利用 杜教篩 可以在低於線性時間的複雜度內求出這些字首和。
對於數論函式 \(h=f\ast g\),要求我們計算 \(\displaystyle\sum\limits_{i=1}^{n}h(i)\)。
記 \(S(n)=\displaystyle\sum_{i=1}^{n}f(i)\)
想辦法構造一個 \(S(n)\) 關於 \(S(\lfloor\frac{n}{i}\rfloor)\) 的遞推式。
\[\sum\limits_{i=1}^{n}h(i)=\sum\limits_{i=1}^{n}\sum_{d|i}f(d)·g(\frac{i}{d})=\sum\limits_{d=1}^{n}g(d)·\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}f(i)=\sum\limits_{d=1}^{n}g(d)·S\Big(\Big\lfloor\frac{n}{d}\Big\rfloor\Big) \]證明:
\(f(d)g(\frac{i}{d})\)
可以得到遞推式:
\[S(n)=g(1)S(n)=\sum\limits_{i=1}^{n}(f\ast g)(i)-\sum\limits_{i=2}^{n}g(i)S\Big(\Big\lfloor\frac{n}{i}\Big\rfloor\Big) \]假如我們可以快速對 \(\displaystyle\sum\limits_{i=1}^{n}(f\ast g)(i)\) 求和,並用數論分塊求解 \(\displaystyle\sum\limits_{i=2}^{n}g(i)S\Big(\Big\lfloor\frac{n}{i}\Big\rfloor\Big)\) 就可以在較短時間內求得 \(g(1)S(n)\)。
莫比烏斯函式字首和
由狄利克雷卷積,我們知道:
\[\epsilon=\mu\ast I\Longleftrightarrow\epsilon(n)=[n=1]\Longleftrightarrow\epsilon(n)=\sum\limits_{d|n}\mu(d)\\S_2(n)=\sum\limits_{i=1}^{n}\epsilon(i)-\sum\limits_{i=2}^{n}S_1\big(\big\lfloor\frac{n}{i}\big\rfloor\big) \]由於 \(\lfloor\frac{n}{i}\rfloor\) 最多隻有 \(O(\sqrt{n})\) 種取值,可以用數論分塊來計算每一項的值。
直接計算的時間複雜度為 \(O(n^{\frac{3}{4}})\)。考慮先線性篩預處理出前 \(n^{\frac{2}{3}}\) 項,剩餘部分的時間複雜度為 \(O\Big(\displaystyle\int_{0}^{n^{\frac{1}{3}}}\sqrt{\frac{n}{x}}dx\Big)=O(n^{\frac{2}{3}})\)。
對於較大的值,需要用 \(\text{map}\) 存下其對應的值,方便以後使用時直接使用之前計算的結果。
尤拉函式字首和
使用杜教篩求解
記 \(S_1(i)=\displaystyle\sum\limits_{i=1}^{n}\varphi(i)\)。
由於 \(\varphi \ast I=\mathbf{id}\),所以:
\[\displaystyle\sum\limits_{i=1}^{n}(\varphi\ast I)(i)=\sum\limits_{i=1}^{n}I(i)· S\Big(\Big\lfloor\frac{n}{i}\Big\rfloor\Big)\\\sum\limits_{i=1}^{n}\mathbf{id}(i)=\sum\limits_{i=1}^{n}I(i)·S\Big(\Big\lfloor\frac{n}{i}\Big\rfloor\Big)\\\frac{n(n+1)}{2}=\sum\limits_{i=1}^{n}S\Big(\Big\lfloor\frac{n}{i}\Big\rfloor\Big)\\S_1(n)=\sum_{i=1}^{n}\mathbf{id}(i)-\sum\limits_{i=2}^{n}I(i)S\Big(\Big\lfloor\frac{n}{i}\Big\rfloor\Big)=\frac{n(n+1)}{2}-\sum\limits_{i=2}^{n}S\Big(\Big\lfloor\frac{n}{i}\Big\rfloor\Big) \]使用莫比烏斯反演求解
\[\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[\gcd(i,j)=1]=\sum\limits_{d=1}^{n}\mu(d)\lfloor\frac{n}{d}\rfloor^{2} \]由於題目所求的是 \(\displaystyle\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i}1·[\gcd(i,j)=1]\),所以我們排除掉 \(i=1,j=1\) 的情況,並將結果除以 \(2\) 即可。
觀察到,只需求出莫比烏斯函式的字首和,就可以快速計算出尤拉函式的字首和了。時間複雜度 \(O(n^{\frac{2}{3}})\) 。
程式碼
#include<bits/stdc++.h>
using namespace std;
const int N=2000010;//n^(2/3)
bool vis[N];
long long n,prime[N+10],mu[N+10],sum[N+10],cnt;
map<long long,long long> mp;
long long S_mu(long long n)
{
if(n<N)
return sum[n];
if(mp[n])
return mp[n];
long long ans=1;
for(long long l=2,r;l<=n;l=r+1)
{
r=n/(n/l);
ans=ans-S_mu(n/l)*(r-l+1);
}
mp[n]=ans;
return ans;
}
long long S_phi(long long n)
{
long long ans=0;
for(long long l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans=ans+(S_mu(r)-S_mu(l-1))*(n/l)*(n/l);
}
return (ans-1)/2+1;
}
void init()
{
mu[1]=1;
for(int i=2;i<=N;i++)
{
if(!vis[i])
{
prime[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt&&i*prime[j]<=N;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)
break;
else
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=N;i++)
sum[i]=sum[i-1]+mu[i];
}
int main()
{
init();
int T;
cin>>T;
while(T--)
{
scanf("%lld",&n);
printf("%lld %lld\n",S_phi(n),S_mu(n));
}
return 0;
}