设
d
(
x
)
d(x)
d(x) 为
x
x
x 的约数个数,求:
a
n
s
=
∑
i
=
1
n
∑
j
=
1
m
d
(
i
j
)
ans=\sum_{i=1}^{n}\sum_{j=1}^{m}d(ij)
ans=i=1∑nj=1∑md(ij)
每个读入文件有
T
T
T 组测试数据,
T
,
n
,
m
≤
50000
T,n,m≤50000
T,n,m≤50000。
Solution
众所周知:
d
(
i
j
)
=
∑
x
∣
i
∑
y
∣
j
[
g
c
d
(
x
,
y
)
=
1
]
d(ij)=\sum_{x|i}\sum_{y|j}[gcd(x,y)=1]
d(ij)=x∣i∑y∣j∑[gcd(x,y)=1]
证明:
考虑每个数对
(
x
,
y
)
(x,y)
(x,y) 对应的约数是什么。
因为
g
c
d
(
x
,
y
)
=
1
gcd(x,y)=1
gcd(x,y)=1,所以对于任意一个质因子
p
p
p,要么
x
x
x 含有
p
p
p,要么
y
y
y 含有
p
p
p,不可能
x
,
y
x,y
x,y 同时含有
p
p
p。
如果包含
p
p
p 的是
x
x
x 且
x
x
x含有
a
a
a 个质因子,那么对应约数中含有
a
a
a 个质因子
p
p
p。
否则设
i
i
i 含有
b
b
b 个质因子
p
p
p,
y
y
y 含有
c
c
c 个质因子
p
p
p,那么对应约数中含有
b
+
c
b+c
b+c 个质因子
p
p
p。
因此数对
(
x
,
y
)
(x,y)
(x,y) 一一对应了
i
j
ij
ij 的每个约数。
那么下面开始推式子:
a
n
s
=
∑
i
=
1
n
∑
j
=
1
m
∑
x
∣
i
∑
y
∣
j
[
g
c
d
(
x
,
y
)
=
1
]
ans=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}[gcd(x,y)=1]
ans=i=1∑nj=1∑mx∣i∑y∣j∑[gcd(x,y)=1]
我们把
[
g
c
d
(
x
,
y
)
=
1
]
[gcd(x,y)=1]
[gcd(x,y)=1] 换成含
μ
μ
μ 的形式,然后我们枚举
x
,
y
x,y
x,y 分别是
d
d
d 的几倍:
a
n
s
=
∑
d
=
1
m
i
n
(
n
,
m
)
μ
(
d
)
∑
x
=
1
⌊
n
d
⌋
⌊
⌊
n
d
⌋
x
⌋
∑
y
=
1
⌊
m
d
⌋
⌊
⌊
n
d
⌋
y
⌋
ans=\sum_{d=1}^{min(n,m)}μ(d)\sum_{x=1}^{\lfloor\frac{n}{d}\rfloor}\lfloor\frac{\lfloor\frac{n}{d}\rfloor}{x}\rfloor\sum_{y=1}^{\lfloor\frac{m}{d}\rfloor}\lfloor\frac{\lfloor\frac{n}{d}\rfloor}{y}\rfloor
ans=d=1∑min(n,m)μ(d)x=1∑⌊dn⌋⌊x⌊dn⌋⌋y=1∑⌊dm⌋⌊y⌊dn⌋⌋
显然不同的
⌊
n
d
⌋
\lfloor\frac{n}{d}\rfloor
⌊dn⌋ 只有
O
(
n
)
O(\sqrt n)
O(n) 种(把
n
n
n 换成
m
m
m 也一样)。
那么我们用
o
(
n
n
)
o(n\sqrt n)
o(nn) 的时间预处理出所有的
∑
x
=
1
n
⌊
⌊
n
d
⌋
x
⌋
\sum_{x=1}^{n}\lfloor\frac{\lfloor\frac{n}{d}\rfloor}{x}\rfloor
∑x=1n⌊x⌊dn⌋⌋,然后对
⌊
n
d
⌋
\lfloor\frac{n}{d}\rfloor
⌊dn⌋ 整除分块,就可以
O
(
T
n
)
O(T\sqrt n)
O(Tn) 回答所有询问。
Code
#include<bits/stdc++.h>usingnamespace std;#define ll long longconstint e =1e5+5, lim =50000;int n, m;
ll ans, f[e], sum[e], miu[e];bool b[e];inlinevoidinit(){int i, j;
miu[1]=1;for(i =2; i <= lim; i++) miu[i]=1;for(i =2; i <= lim; i++)if(!b[i]){
miu[i]=-1;for(j =2* i; j <= lim; j += i){if((j / i)% i ==0) miu[j]=0;else miu[j]*=-1;
b[j]=1;}}for(i =1; i <= lim; i++)for(j = i; j <= lim; j += i)
f[j]++;for(i =1; i <= lim; i++){
sum[i]= sum[i -1]+ miu[i];
f[i]+= f[i -1];}}intmain(){init();int i, tst, j;scanf("%d",&tst);while(tst--){scanf("%d%d",&n,&m);
ans =0;int tmp =min(n, m);for(i =1; i <= tmp; i = j +1){
j =min(n /(n / i), m /(m / i));
ans +=(sum[j]- sum[i -1])* f[n / i]* f[m / i];}printf("%lld\n", ans);}return0;}