题面
题解
明显地,这个QQ数可以用 μ \mu μ 表示,于是询问就变成了这样:
∑
i
=
1
n
∑
d
∣
i
(
1
−
μ
(
d
)
2
)
=
∑
d
=
1
n
⌊
n
d
⌋
(
1
−
μ
(
d
)
2
)
\begin{aligned} & \sum_{i=1}^n\sum_{d|i}\left(1-\mu(d)^2\right)\\ =& \sum_{d=1}^n\left\lfloor\frac{n}{d}\right\rfloor\left(1-\mu(d)^2\right) \end{aligned}
=i=1∑nd∣i∑(1−μ(d)2)d=1∑n⌊dn⌋(1−μ(d)2)
发现
⌊
n
d
⌋
\lfloor\dfrac{n}{d}\rfloor
⌊dn⌋ 是可以整除分块的,但是
n
≤
1
0
9
n\leq 10^9
n≤109 无法线性筛
μ
\mu
μ。
考虑如何更快地求出 ∑ i = 1 n ( 1 − μ ( d ) 2 ) \sum\limits_{i=1}^n\left(1-\mu(d)^2\right) i=1∑n(1−μ(d)2),也就是 1 ∼ n 1\sim n 1∼n 中的QQ数个数。
但是有这么一个神奇的式子:
∑
i
=
1
n
(
1
−
μ
(
i
)
2
)
=
∑
x
=
2
n
−
μ
(
x
)
⌊
N
x
2
⌋
\sum_{i=1}^n\left(1-\mu(i)^2\right)=\sum_{x=2}^{\sqrt n}-\mu(x)\lfloor\frac{N}{x^2}\rfloor
i=1∑n(1−μ(i)2)=x=2∑n−μ(x)⌊x2N⌋
证明:
首先,对于每一个QQ数 a a a,他肯定可以拆分成这种形式: a = x 2 y a=x^2 y a=x2y(其中 x x x 不是QQ数),也就是说,我们把每一个QQ数表示成一个非QQ数的平方再乘上一个数。
比如QQ数 2 4 × 3 3 × 5 2^4\times 3^3\times 5 24×33×5 可以拆分为 ( 2 × 3 ) 2 × … (2\times 3)^2\times \dots (2×3)2×…( x = 2 × 3 x=2\times 3 x=2×3)或者 2 2 × … 2^2\times \dots 22×…( x = 2 x=2 x=2)或者 3 2 × … 3^2\times \dots 32×…( x = 3 x=3 x=3)。
那么 ∑ x = 2 n − μ ( x ) ⌊ N x 2 ⌋ \sum\limits_{x=2}^{\sqrt n}-\mu(x)\lfloor\dfrac{N}{x^2}\rfloor x=2∑n−μ(x)⌊x2N⌋ 就可以看成是枚举每一个 x x x,然后再枚举有多少个 a a a,然后再配上一个 − μ ( x ) -\mu(x) −μ(x) 去重。(至于能去重的原因结合 μ \mu μ 的定义以及这个式子想一想: ∑ i = 1 n ( − 1 ) i ( n i ) = − 1 \sum\limits_{i=1}^n(-1)^i\dbinom{n}{i}=-1 i=1∑n(−1)i(in)=−1)
那么就能在 n \sqrt n n 的时间内求出 ∑ i = 1 n ( 1 − μ ( d ) 2 ) \sum\limits_{i=1}^n\left(1-\mu(d)^2\right) i=1∑n(1−μ(d)2) 了。
时间复杂度比较玄学,反正能过。
代码如下:
#include<bits/stdc++.h>
#define SN 32000
#define ll long long
using namespace std;
int ql,qr;
int cnt,prime[SN],mu[SN];
bool notprime[SN];
void init()
{
mu[1]=1;
for(int i=2;i<=31622;i++)
{
if(!notprime[i])
{
prime[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt&&i*prime[j]<=31622;j++)
{
notprime[i*prime[j]]=1;
if(!(i%prime[j])) break;
mu[i*prime[j]]=-mu[i];
}
}
}
int summu(int n)
{
ll ans=0;
for(int i=2;i*i<=n;i++)
ans-=mu[i]*(n/i/i);
return ans;
}
ll solve(int n)
{
ll ans=0;
for(int l=1,r,last=0;l<=n;l=r+1)
{
r=n/(n/l);
ll tmp=summu(r);
ans+=(n/l)*(tmp-last);
last=tmp;
}
return ans;
}
int main()
{
init();
scanf("%d%d",&ql,&qr);
printf("%lld\n",solve(qr)-solve(ql-1));
return 0;
}