题目链接:传送门
考虑点
(
x
,
y
)
(x,y)
(x,y)对答案的贡献:
设
g
c
d
(
x
,
y
)
=
k
gcd(x,y)=k
gcd(x,y)=k,
x
=
a
k
,
y
=
b
k
.
x=ak,y=bk.
x=ak,y=bk.
若有
x
′
,
y
′
x',y'
x′,y′满足
(
x
′
,
y
′
)
(x',y')
(x′,y′)在
(
0
,
0
)
(0,0)
(0,0)到
(
x
,
y
)
(x,y)
(x,y)的直线上,则有
y
′
x
′
=
y
x
=
b
a
\large\frac{y'}{x'}=\frac{y}{x}=\frac{b}{a}
x′y′=xy=ab
则
(
x
′
,
y
′
)
(x',y')
(x′,y′)珂以取的值为:
(
a
,
b
)
,
(
2
a
,
2
b
)
,
.
.
.
,
(
(
k
−
1
)
a
,
(
k
−
1
)
b
)
(a,b),(2a,2b),...,((k-1)a,(k-1)b)
(a,b),(2a,2b),...,((k−1)a,(k−1)b)
所以
(
0
,
0
)
(0,0)
(0,0)到
(
x
,
y
)
(x,y)
(x,y)共有
k
−
1
k-1
k−1个点,即
g
c
d
(
x
,
y
)
−
1
gcd(x,y)-1
gcd(x,y)−1个点qwq。
所以答案变成:
a
n
s
=
Σ
i
=
1
N
Σ
j
=
1
M
(
2
(
g
c
d
(
i
,
j
)
−
1
)
+
1
)
ans=\Large\Sigma\large_{i=1}^{N}\Large\Sigma\large_{j=1}^M(2(gcd(i,j)-1)+1)
ans=Σi=1NΣj=1M(2(gcd(i,j)−1)+1)
=
2
Σ
i
=
1
N
Σ
i
=
1
M
g
c
d
(
i
,
j
)
−
N
∗
M
=2\Large\Sigma\large_{i=1}^N\Large\Sigma\large_{i=1}^Mgcd(i,j)-N*M
=2Σi=1NΣi=1Mgcd(i,j)−N∗M
调换一下枚举顺序,珂以得到
a
n
s
=
2
Σ
t
=
1
m
i
n
(
N
,
M
)
t
(
Σ
i
=
1
N
Σ
j
=
1
M
[
g
c
d
(
i
,
j
)
=
=
t
]
)
−
N
∗
M
ans=2\Large\Sigma\large_{t=1}^{min(N,M)}t(\Large\Sigma\large_{i=1}^N\Large\Sigma\large_{j=1}^M[gcd(i,j)==t])-N*M
ans=2Σt=1min(N,M)t(Σi=1NΣj=1M[gcd(i,j)==t])−N∗M
对这坨东西大莉莫比乌斯反演,乱搞一波(乱搞过程:蒟蒻的博客qwq)
得到
a
n
s
=
Σ
t
=
1
m
i
n
(
N
,
M
)
(
t
Σ
t
∣
d
μ
(
d
t
)
⌊
N
d
⌋
⌊
M
d
⌋
)
−
N
∗
M
ans=\Large\Sigma\large_{t=1}^{min(N,M)}(t\Large\Sigma\large_{t|d}\mu(\frac{d}{t})\lfloor\frac{N}{d}\rfloor\lfloor\frac{M}{d}\rfloor)-N*M
ans=Σt=1min(N,M)(tΣt∣dμ(td)⌊dN⌋⌊dM⌋)−N∗M
然后每次大莉枚举
t
t
t,对括号里的部分大莉枚举
t
t
t的倍数即可。
珂以证明这样时间复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的qwq
话说这道题好像还有一个查询只用
O
(
n
)
O(\sqrt n)
O(n)的神仙做法……比较玄学qwq
毒瘤代码
#include<stdio.h>
#include<cstring>
#include<algorithm>
#define re register int
#define rl register ll
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=100005;
int tot,prime[Size],mu[Size];
bool vis[Size];
void getp(int maxn) {
mu[1]=1;
for(re i=2; i<=maxn; i++) {
if(!vis[i]) {
prime[++tot]=i;
mu[i]=-1;
}
for(re j=1; j<=tot && i*prime[j]<=maxn; j++) {
vis[i*prime[j]]=true;
if(i%prime[j]==0) break;
mu[i*prime[j]]=-mu[i];
}
}
}
int main() {
int n=read();
int m=read();
int minn=min(n,m);
getp(minn);
ll ans=0;
for(re t=1; t<=minn; t++) {
int lst;
ll sum=0;
for(re i=t; i<=minn; i+=t) {
sum+=(ll)mu[i/t]*(n/i)*(m/i);
}
ans+=sum*t;
}
printf("%lld",(ans<<1ll)-(ll)n*m);
return 0;
}