Description
设d(x)为x的约数个数,给定N、M,求 ∑ni=1∑mj=1d(ij) ∑ i = 1 n ∑ j = 1 m d ( i j )
1<=N, M<=50000
1<=T<=50000
Solution
首先要知道一个结论:
d(nm)=∑i|n∑j|m[gcd(i,j)==1]
d
(
n
m
)
=
∑
i
|
n
∑
j
|
m
[
g
c
d
(
i
,
j
)
==
1
]
证明可以考虑nm质因数分解得到 nm=p1k1p2k2...pnkn n m = p 1 k 1 p 2 k 2 . . . p n k n ,约数个数就是 ∏(ki+1) ∏ ( k i + 1 ) 。这里可以感性地看成是枚举其中某两段互质的
那么有
ans=∑i=1n∑j=1md(ij)=∑i=1n∑j=1m∑p|i∑q|j[gcd(p,q)==1]
a
n
s
=
∑
i
=
1
n
∑
j
=
1
m
d
(
i
j
)
=
∑
i
=
1
n
∑
j
=
1
m
∑
p
|
i
∑
q
|
j
[
g
c
d
(
p
,
q
)
==
1
]
回忆一下μ的性质有
ans=∑i=1n∑j=1m∑p|i∑q|j∑x|gcd(p,q)μ(x)
a
n
s
=
∑
i
=
1
n
∑
j
=
1
m
∑
p
|
i
∑
q
|
j
∑
x
|
g
c
d
(
p
,
q
)
μ
(
x
)
套路一波把x拉出来
ans=∑i=1n∑j=1m∑x|gcd(i,j)d(ix)d(jx)μ(x)
a
n
s
=
∑
i
=
1
n
∑
j
=
1
m
∑
x
|
g
c
d
(
i
,
j
)
d
(
i
x
)
d
(
j
x
)
μ
(
x
)
这里为什么是 d(ix) d ( i x ) 可以考虑转化之前p满足 x|p x | p 且 p|i p | i ,那么就是求满足 xk|i x k | i 的k的数量,也即 k|ix k | i x 的数量
再套路一波继续把x拉出来
ans=∑x=1min(n,m)μ(x)∑i=1⌊nx⌋d(i)∑j=1⌊mx⌋d(j)
a
n
s
=
∑
x
=
1
m
i
n
(
n
,
m
)
μ
(
x
)
∑
i
=
1
⌊
n
x
⌋
d
(
i
)
∑
j
=
1
⌊
m
x
⌋
d
(
j
)
可以发现d(x)是积性函数,预处理一波前缀和套分块就ok
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
typedef long long LL;
const int N=50005;
int e[N+5],prime[N+5],d[N+5],mu[N+5];
bool not_prime[N+5];
void pre_work() {
mu[1]=1; d[1]=1;
rep(i,2,N-5) {
if (!not_prime[i]) {
prime[++prime[0]]=i;
mu[i]=-1;
d[i]=2;
e[i]=1;
}
for (int j=1;j<=prime[0]&&i*prime[j]<=N;j++) {
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) {
mu[i*prime[j]]=0;
d[i*prime[j]]=d[i]/(e[i]+1)*(e[i]+2);
e[i*prime[j]]=e[i]+1;
break;
}
mu[i*prime[j]]=-mu[i];
d[i*prime[j]]=d[i]*d[prime[j]];
e[i*prime[j]]=1;
}
}
rep(i,1,N) d[i]+=d[i-1];
rep(i,1,N) mu[i]+=mu[i-1];
}
void solve(int n,int m) {
if (n>m) std:: swap(n,m);
LL ans=0;
for (int i=1,j;i<=n;i=j+1) {
j=std:: min(n/(n/i),m/(m/i));
ans+=(LL)((LL)mu[j]-(LL)mu[i-1])*(LL)((LL)d[n/i]*(LL)d[m/i]);
}
printf("%lld\n", ans);
}
int main(void) {
pre_work();
int T; scanf("%d",&T);
while (T--) {
int n,m; scanf("%d%d",&n,&m);
solve(n,m);
}
return 0;
}