Time Limit: 20 Sec
Memory Limit: 128 MB
Description
设d(x)为x的约数个数,给定N、M,求 ∑ i = 1 n ∑ j = 1 m d ( i j ) \sum_{i=1}^n\sum_{j=1}^md(ij) ∑i=1n∑j=1md(ij)
Input
输入文件包含多组测试数据。
第一行,一个整数T,表示测试数据的组数。
接下来的T行,每行两个整数N、M。
Output
T行,每行一个整数,表示你所求的答案。
HINT
1<=N, M<=50000
1<=T<=50000
题目分析
在蒟蒻望着屏幕发呆对此题无从下手半个小时后
点开题解发现上来就是一个结论之时内心是无比复杂的,复杂到已经语无伦次。。。
先看看这个蒟蒻不懂怎么证得结论
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]
有了这个结论一切都好办了,直接带入
∑
i
=
1
n
∑
j
=
1
m
∑
x
∣
i
∑
y
∣
j
[
g
c
d
(
x
,
y
)
=
1
]
\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[gcd(x,y)=1]
i=1∑nj=1∑mx∣i∑y∣j∑[gcd(x,y)=1]
再莫比乌斯反演
∑
i
=
1
n
∑
j
=
1
m
∑
x
∣
i
∑
y
∣
j
∑
d
∣
g
c
d
(
x
,
y
)
μ
(
d
)
\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}\sum_{d|gcd(x,y)}\mu(d)
i=1∑nj=1∑mx∣i∑y∣j∑d∣gcd(x,y)∑μ(d)
把枚举
i
,
j
i,j
i,j的约数变成枚举
x
,
y
x,y
x,y的倍数
∑
i
=
1
n
∑
j
=
1
m
∑
x
=
1
i
∑
y
=
1
j
[
x
∣
i
]
[
y
∣
j
]
∑
d
=
1
g
c
d
(
x
,
y
)
μ
(
d
)
[
d
∣
g
c
d
(
x
,
y
)
]
\sum_{i=1}^n\sum_{j=1}^m\sum_{x=1}^i\sum_{y=1}^j[x|i][y|j]\sum_{d=1}^{gcd(x,y)}\mu(d)[d|gcd(x,y)]
i=1∑nj=1∑mx=1∑iy=1∑j[x∣i][y∣j]d=1∑gcd(x,y)μ(d)[d∣gcd(x,y)]
∑
x
=
1
n
∑
y
=
1
m
∑
t
1
=
1
⌊
n
x
⌋
∑
t
2
=
1
⌊
m
y
⌋
∑
d
=
1
g
c
d
(
x
,
y
)
μ
(
d
)
[
d
∣
g
c
d
(
x
,
y
)
]
\sum_{x=1}^n\sum_{y=1}^m\sum_{t_1=1}^{\lfloor\frac{n}{x}\rfloor}\sum_{t_2=1}^{\lfloor\frac{m}{y}\rfloor}\sum_{d=1}^{gcd(x,y)}\mu(d)[d|gcd(x,y)]
x=1∑ny=1∑mt1=1∑⌊xn⌋t2=1∑⌊ym⌋d=1∑gcd(x,y)μ(d)[d∣gcd(x,y)]
把d的枚举提前,把枚举
x
,
y
x,y
x,y变成枚举
d
d
d的倍数
∑
d
=
1
m
i
n
(
n
,
m
)
∑
x
=
1
n
∑
y
=
1
m
∑
t
1
=
1
⌊
n
x
⌋
∑
t
2
=
1
⌊
m
y
⌋
μ
(
d
)
[
d
∣
g
c
d
(
x
,
y
)
]
\sum_{d=1}^{min(n,m)}\sum_{x=1}^n\sum_{y=1}^m\sum_{t_1=1}^{\lfloor\frac{n}{x}\rfloor}\sum_{t_2=1}^{\lfloor\frac{m}{y}\rfloor}\mu(d)[d|gcd(x,y)]
d=1∑min(n,m)x=1∑ny=1∑mt1=1∑⌊xn⌋t2=1∑⌊ym⌋μ(d)[d∣gcd(x,y)]
∑ d = 1 m i n ( n , m ) ∑ k 1 = 1 ⌊ n d ⌋ ∑ k 2 = 1 ⌊ m d ⌋ ∑ t 1 = 1 ⌊ n d k 1 ⌋ ∑ t 2 = 1 ⌊ m d k 2 ⌋ μ ( d ) = ∑ d = 1 m i n ( n , m ) μ ( d ) ∑ k 1 = 1 ⌊ n d ⌋ ∑ k 2 = 1 ⌊ m d ⌋ ∑ t 1 = 1 ⌊ n d k 1 ⌋ ∑ t 2 = 1 ⌊ m d k 2 ⌋ 1 \sum_{d=1}^{min(n,m)}\sum_{k_1=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{k_2=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{t_1=1}^{\lfloor\frac{n}{dk_1}\rfloor}\sum_{t_2=1}^{\lfloor\frac{m}{dk_2}\rfloor}\mu(d)=\sum_{d=1}^{min(n,m)}\mu(d)\sum_{k_1=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{k_2=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{t_1=1}^{\lfloor\frac{n}{dk_1}\rfloor}\sum_{t_2=1}^{\lfloor\frac{m}{dk_2}\rfloor}1 d=1∑min(n,m)k1=1∑⌊dn⌋k2=1∑⌊dm⌋t1=1∑⌊dk1n⌋t2=1∑⌊dk2m⌋μ(d)=d=1∑min(n,m)μ(d)k1=1∑⌊dn⌋k2=1∑⌊dm⌋t1=1∑⌊dk1n⌋t2=1∑⌊dk2m⌋1
看起来不太简洁啊,令 T 1 = ⌊ n d ⌋ , T 2 = ⌊ m d ⌋ T_1=\lfloor\frac{n}{d}\rfloor,T_2=\lfloor\frac{m}{d}\rfloor T1=⌊dn⌋,T2=⌊dm⌋
∑ d = 1 m i n ( n , m ) μ ( d ) ∑ i = 1 T 1 ∑ j = 1 T 2 ∑ x = 1 ⌊ T 1 i ⌋ ∑ y = 1 ⌊ T 2 j ⌋ 1 = ∑ d = 1 m i n ( n , m ) μ ( d ) ∑ i = 1 T 1 ⌊ T 1 i ⌋ ∑ j = 1 T 2 ⌊ T 2 j ⌋ \sum_{d=1}^{min(n,m)}\mu(d)\sum_{i=1}^{T_1}\sum_{j=1}^{T_2}\sum_{x=1}^{\lfloor\frac{T_1}{i}\rfloor}\sum_{y=1}^{\lfloor\frac{T_2}{j}\rfloor}1=\sum_{d=1}^{min(n,m)}\mu(d)\sum_{i=1}^{T_1}\lfloor\frac{T_1}{i}\rfloor\sum_{j=1}^{T_2}\lfloor\frac{T_2}{j}\rfloor d=1∑min(n,m)μ(d)i=1∑T1j=1∑T2x=1∑⌊iT1⌋y=1∑⌊jT2⌋1=d=1∑min(n,m)μ(d)i=1∑T1⌊iT1⌋j=1∑T2⌊jT2⌋
预处理 μ ( i ) \mu(i) μ(i)的前缀和以及 g ( n ) = ∑ i = 1 n ⌊ n i ⌋ g(n)=\sum_{i=1}^n\lfloor\frac{n}{i}\rfloor g(n)=∑i=1n⌊in⌋之后整除分块求解答案即可
这里
g
(
n
)
g(n)
g(n)预处理复杂度是
O
(
n
n
)
O(n\sqrt n)
O(nn),其实是还有优化的余地的
仔细观察可以发现
g
(
n
)
g(n)
g(n)其实就是
∑
i
=
1
n
d
(
i
)
\sum_{i=1}^nd(i)
∑i=1nd(i)(其中d(i)为i的约数个数)
由于约数个数是积性函数,完全可以与莫比乌斯函数一起处理
(然而因为蒟蒻比较懒就没写了)
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=50010;
int T;
int miu[maxn];
int vis[maxn],prim[maxn],cnt;
lt sum[maxn],ds[maxn];
void Miu(int n)
{
miu[1]=1;
for(int i=2;i<=n;++i)
{
if(!vis[i]) prim[++cnt]=i,miu[i]=-1;
for(int j=1;j<=cnt;++j)
{
if(i*prim[j]>n) break;
vis[i*prim[j]]=1;
if(i%prim[j]==0) break;
else miu[i*prim[j]]=-miu[i];
}
}
for(int i=1;i<=n;++i)
sum[i]=sum[i-1]+miu[i];
for(int i=1;i<=n;++i)
for(int ll=1,rr;ll<=i;ll=rr+1)
{
rr=i/(i/ll);
ds[i]+=1ll*(i/ll)*(rr-ll+1);
}
}
lt query(int n,int m)
{
lt res=0; int lim=min(n,m);
for(int ll=1,rr;ll<=lim;ll=rr+1)
{
rr=min(n/(n/ll),m/(m/ll));
res+=1ll*(sum[rr]-sum[ll-1])*ds[n/ll]*ds[m/ll];
}
return res;
}
int main()
{
T=read(); Miu(maxn-5);
while(T--)
{
int n=read(),m=read();
printf("%lld\n",query(n,m));
}
return 0;
}