【XSY2719】prime(积性函数,莫比乌斯反演)
题意:求 \(\left(\sum\limits_{i=1}^n2^{f(i)}\right)\bmod 998244353\),其中 \(f(i)\) 表示 \(i\) 的不同质因子的个数,\(n\leq 10^{12}\)。
一开始发现 \(g(x)=2^{f(x)}\) 是积性函数以为能直接 min_25 筛(
结果时间爆炸(
考虑 \(2^{f(i)}\) 的组合意义,得到 \(2^{f(i)}=\sum\limits_{j|i}\mu^2(j)\)。
于是:
\[\begin{aligned}
&\sum_{i=1}^n2^{f(i)}\\
=&\sum_{i=1}^n\sum_{j|i}\mu^2(j)\\
=&\sum_{i=1}^n\lfloor\dfrac{n}{i}\rfloor\mu^2(i)
\end{aligned}
\]
如果我们能快速求出 \(\mu^2(i)\) 在 \(n\) 的基本和组上的前缀和,就能整除分块了。
注意到设 \(d\) 为 \(x\) 的最大平方因子(如 \(x=3\) 时 \(d=1\),\(x=24\) 时 \(d=2\)),那么:
\[\mu^2(x)=[d=1]=\sum_{i|d}\mu(i)
\]
根据 \(d\) 的定义,\(x\) 可以写成 \(y\times d^2\) 的形式,其中 \(y\) 没有平方因子。
那么枚举 \(i|d\) 相当于枚举 \(i^2|x\),于是 \(\mu^2(x)=\sum\limits_{i^2|x}\mu(i)\)。
于是原式为:
\[\begin{aligned}
=&\sum_{i=1}^n\lfloor\dfrac{n}{i}\rfloor \sum_{d^2|i}\mu(d)\\
=&\sum_{d=1}^{\sqrt n}\mu(d)\sum_{j=1}^{n/(d^2)}\lfloor\dfrac{n}{d^2j}\rfloor
\end{aligned}
\]
设 \(g(n)=\sum\limits_{i=1}^n\lfloor\dfrac{n}{i}\rfloor\),于是原式为:
\[=\sum_{d=1}^{\sqrt n}\mu(d)g\left(\lfloor\dfrac{n}{d^2}\rfloor\right)
\]
我们暴力枚举每一个 \(d\),并每次暴力整除分块求出 \(g\) 即可。
时间复杂度:
\[\sum _{i=1}^{\sqrt n}O\left(\sqrt{\dfrac{n}{i^2}}\right)=\sum_{i=1}^{\sqrt n}O\left(\dfrac{\sqrt n}{i}\right)=O\left(\sqrt n\ln \sqrt n\right)
\]
代码如下:
#include<bits/stdc++.h>
#define N 1000010
#define ll long long
using namespace std;
namespace modular
{
const int mod=998244353;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
}using namespace modular;
ll n;
int sn;
int cnt,prime[N],mu[N];
bool notprime[N];
void init()
{
mu[1]=1;
for(int i=2;i<=sn;i++)
{
if(!notprime[i])
{
prime[++cnt]=i;
mu[i]=mod-1;
}
for(int j=1,x;j<=cnt&&(x=i*prime[j])<=sn;j++)
{
notprime[x]=1;
if(!(i%prime[j])) break;
mu[x]=mul(mu[i],mu[prime[j]]);
}
}
}
int g(ll n)
{
int ans=0;
for(ll l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans=add(ans,mul((r-l+1)%mod,(n/l)%mod));
}
return ans;
}
int main()
{
scanf("%lld",&n);
sn=sqrt(n);
init();
int ans=0;
for(int d=1;d<=sn;d++)
ans=add(ans,mul(mu[d],g(n/d/d)));
printf("%d\n",ans);
return 0;
}