题目链接:哆啦A梦传送门
题意:给出n,m,求
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
为
素
数
]
\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)为素数]
∑i=1n∑j=1m[gcd(i,j)为素数]
n
,
m
<
=
1
e
7
n,m<=1e7
n,m<=1e7
依题意得:
s
u
m
=
∑
p
∈
素
数
∑
i
=
1
n
∑
j
=
1
m
g
c
d
(
i
,
j
)
=
p
\begin{aligned} sum&=\sum_{p\in素数}\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)=p\\ \end{aligned}
sum=p∈素数∑i=1∑nj=1∑mgcd(i,j)=p
这里我们用莫比乌斯反演第二描述去做。
F
(
n
)
=
∑
n
∣
d
f
(
d
)
,
f
(
n
)
=
∑
n
∣
d
u
(
d
n
)
F
(
d
)
F(n)=\sum_{n|d}f(d), f(n)=\sum_{n|d}u(\frac{d}{n})F(d)
F(n)=∑n∣df(d),f(n)=∑n∣du(nd)F(d)
设 f(i)表示满足
g
c
d
(
x
,
y
)
=
i
gcd(x,y)=i
gcd(x,y)=i时(x,y)的对数。
则F(i)表示满足
i
∣
g
c
d
(
x
,
y
)
i|gcd(x,y)
i∣gcd(x,y)时(x,y)的对数。我们易得:
F
(
i
)
=
⌊
n
i
⌋
⌊
m
i
⌋
F(i)=\left \lfloor \frac{n}{i} \right \rfloor \left \lfloor \frac{m}{i} \right \rfloor
F(i)=⌊in⌋⌊im⌋
故 f ( i ) = ∑ i ∣ d u ( d i ) ⌊ n d ⌋ ⌊ m d ⌋ f(i)=\sum_{i|d}u(\frac{d}{i})\left \lfloor \frac{n}{d} \right \rfloor \left \lfloor \frac{m}{d} \right \rfloor f(i)=∑i∣du(id)⌊dn⌋⌊dm⌋
即:我们直接枚举
d
i
\frac{d}{i}
id的倍数d,得:
s
u
m
=
∑
p
∈
素
数
∑
d
=
1
⌊
n
p
⌋
u
(
d
)
⌊
n
p
d
⌋
⌊
m
p
d
⌋
\begin{aligned}sum=\sum_{p\in素数}\sum_{d=1}^{\left \lfloor \frac{n}{p} \right \rfloor}u(d)\left \lfloor \frac{n}{pd} \right \rfloor \left \lfloor \frac{m}{pd} \right \rfloor\end{aligned}
sum=p∈素数∑d=1∑⌊pn⌋u(d)⌊pdn⌋⌊pdm⌋
这个式子复杂度为 O ( 质 数 个 数 ∗ n ) O(质数个数*\sqrt n) O(质数个数∗n),复杂度还是有点大。
我们再令:
k
=
p
d
k=pd
k=pd
那么代入得:
s
u
m
=
∑
k
=
1
n
∑
p
∈
素
数
,
p
∣
k
u
(
k
p
)
⌊
n
k
⌋
⌊
m
k
⌋
\begin{aligned}sum=\sum_{k=1}^{n}\sum_{p\in素数,p|k}u(\frac{k}{p})\left \lfloor \frac{n}{k} \right \rfloor \left \lfloor \frac{m}{k} \right \rfloor\end{aligned}
sum=k=1∑np∈素数,p∣k∑u(pk)⌊kn⌋⌊km⌋
设
f
(
x
)
=
∑
p
∈
素
数
,
p
∣
x
u
(
x
p
)
\begin{aligned}f(x)=\sum_{p\in素数,p|x}u(\frac{x}{p})\end{aligned}
f(x)=p∈素数,p∣x∑u(px),那么
最终
s
u
m
=
∑
k
=
1
n
f
(
k
)
⌊
n
k
⌋
⌊
m
k
⌋
\begin{aligned}sum=\sum_{k=1}^{n}f(k)\left \lfloor \frac{n}{k} \right \rfloor \left \lfloor \frac{m}{k} \right \rfloor\end{aligned}
sum=k=1∑nf(k)⌊kn⌋⌊km⌋
我们只需在线性筛那里把 f ( x ) f(x) f(x)的前缀和弄出来就可以了。
线性筛方程:
f
(
x
)
=
∑
p
∈
素
数
,
p
∣
x
u
(
x
p
)
,
设
x
的
最
小
质
因
子
为
y
,
x
=
i
∗
y
\begin{aligned}f(x)=\sum_{p\in素数,p|x}u(\frac{x}{p}),设x的最小质因子为y,x=i*y\end{aligned}
f(x)=p∈素数,p∣x∑u(px),设x的最小质因子为y,x=i∗y
u
(
1
)
,
x
为
素
数
f
(
x
)
=
u
(
i
)
,
i
%
y
=
=
0
−
f
(
i
)
+
u
(
i
)
,
i
%
y
!
=
0
\begin{aligned} &u(1),&x为素数\\ f(x)=&u(i), &i\%y==0\\ &-f(i)+u(i),&i\%y!=0 \end{aligned}
f(x)=u(1),u(i),−f(i)+u(i),x为素数i%y==0i%y!=0
详情见:神犇
那么我们就能在 O ( n ) O(\sqrt n) O(n)的时间复杂度解决。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define INF 0x3f3f3f3f
const LL mod=1e7+9;
const int N=1e7+10;
int prime[N],tot,mu[N];
bool vis[N];
LL f[N];///预处理函数f的前缀和
void init()
{
mu[1]=1;
for(int i=2;i<N;i++)
{
if(!vis[i]){
prime[++tot]=i;
mu[i]=-1;
f[i]=1;
}
for(int j=1;j<=tot&&i*prime[j]<N;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0){
f[i*prime[j]]=mu[i];
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
f[i*prime[j]]=-f[i]+mu[i];
}
f[i]+=f[i-1];
}
}
LL solve(int n,int m)
{
LL re=0;
int r;
for(int l=1;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
re=re+1LL*(f[r]-f[l-1])*(n/l)*(m/l);
}
return re;
}
int main()
{
init();
int ncase;
scanf("%d",&ncase);
LL n,m;
while(ncase--)
{
scanf("%d%d",&n,&m);
if(n>m) swap(n,m);
printf("%lld\n",solve(n,m));
}
return 0;
}
预处理前缀和,还有另外一种方法:
参考博客:(https://www.cnblogs.com/zyb993963526/p/7253495.html)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define INF 0x3f3f3f3f
const LL mod=1e7+9;
const int N=1e7+10;
int prime[N],tot,mu[N];
bool vis[N];
LL sum[N];///预处理函数f的前缀和
void init()
{
mu[1]=1;
for(int i=2;i<N;i++)
{
if(!vis[i]){
prime[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot&&i*prime[j]<N;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
///预处理函数f的前缀和
sum[0]=0;
for(int i=1;i<=tot;i++)
{
for(int j=prime[i];j<N;j+=prime[i]){
sum[j]+=mu[j/prime[i]];
}
}
for(int i=1;i<N;i++){
sum[i]+=sum[i-1];
}
}
LL solve(int n,int m)
{
LL re=0;
int r;
for(int l=1;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
re=re+1LL*(sum[r]-sum[l-1])*(n/l)*(m/l);
}
return re;
}
int main()
{
init();
int ncase;
scanf("%d",&ncase);
LL n,m;
while(ncase--)
{
scanf("%d%d",&n,&m);
if(n>m) swap(n,m);
printf("%lld\n",solve(n,m));
}
return 0;
}
我的标签:做个有情怀的程序员。