题意
给你n和m求
∑
i
=
1
n
∑
j
=
1
m
d
(
i
j
)
\sum_{i=1}^n\sum_{j=1}^md(ij)
i=1∑nj=1∑md(ij)
其中
d
(
x
)
d(x)
d(x)表示
x
x
x约数的个数
思路
这道题其实有一个很神奇的结论
∑
i
=
1
n
∑
j
=
1
m
d
(
i
j
)
=
∑
g
c
d
(
i
,
j
)
=
=
1
⌊
n
i
⌋
⌊
m
j
⌋
\sum_{i=1}^n\sum_{j=1}^md(ij)=\sum_{gcd(i,j)==1}\left \lfloor \frac{n}{i} \right \rfloor\left \lfloor \frac{m}{j} \right \rfloor
i=1∑nj=1∑md(ij)=gcd(i,j)==1∑⌊in⌋⌊jm⌋
及
∑
i
=
1
n
∑
j
=
1
m
⌊
n
i
⌋
⌊
m
j
⌋
[
g
c
d
(
i
,
j
)
=
=
1
]
\sum_{i=1}^n\sum_{j=1}^m\left \lfloor \frac{n}{i} \right \rfloor\left \lfloor \frac{m}{j} \right \rfloor[gcd(i,j)==1]
i=1∑nj=1∑m⌊in⌋⌊jm⌋[gcd(i,j)==1]
那么有了这个式子之后我们就可做了,已知
∑
d
∣
n
μ
(
d
)
=
[
n
=
=
1
]
\sum_{d|n}\mu(d)=[n==1]
d∣n∑μ(d)=[n==1]
把
n
=
g
c
d
(
i
,
j
)
n=gcd(i,j)
n=gcd(i,j)带入上式有
∑
i
=
1
n
∑
j
=
1
m
⌊
n
i
⌋
⌊
m
j
⌋
∑
d
∣
g
c
d
(
i
,
j
)
μ
(
d
)
\sum_{i=1}^n\sum_{j=1}^m\left \lfloor \frac{n}{i} \right \rfloor\left \lfloor \frac{m}{j} \right \rfloor\sum_{d|gcd(i,j)}\mu(d)
i=1∑nj=1∑m⌊in⌋⌊jm⌋d∣gcd(i,j)∑μ(d)
我们改变枚举变量,令
N
=
m
i
n
(
n
,
m
)
N=min(n,m)
N=min(n,m),原来枚举的是
g
c
d
(
i
,
j
)
gcd(i,j)
gcd(i,j)的因子,而
g
c
d
(
i
,
j
)
gcd(i,j)
gcd(i,j)的范围为
1
∼
N
1\sim N
1∼N,相当于枚举
1
∼
N
1\sim N
1∼N中每个数的因子,这和枚举
1
∼
N
1\sim N
1∼N中每个数的倍数是等价的,故有
=
∑
d
=
1
N
μ
(
d
)
∑
i
=
1
n
d
⌊
n
i
d
⌋
∑
j
=
1
m
d
⌊
m
j
d
⌋
=\sum_{d=1}^N\mu(d) \sum_{i=1}^{\frac{n}{d} }\left \lfloor \frac{n}{id} \right \rfloor\sum_{j=1}^{ \frac{m}{d}}\left \lfloor \frac{m}{jd} \right \rfloor
=d=1∑Nμ(d)i=1∑dn⌊idn⌋j=1∑dm⌊jdm⌋
我们令
f
(
n
)
=
∑
i
=
1
n
⌊
n
i
⌋
f(n)=\sum_{i=1}^{n}\left \lfloor \frac{n}{i} \right \rfloor
f(n)=∑i=1n⌊in⌋
可以看出上式是枚举
1
∼
n
1\sim n
1∼n中每个数的倍数有多少个,这个和每个数的约数有多少个是等价的,所以
f
(
n
)
f(n)
f(n)枚举的就是
1
∼
N
1\sim N
1∼N中每个数的约数和,那么我们只要筛出每个数的约数个数再做一个前缀和就可以得到
f
(
n
)
f(n)
f(n)了
那么原式有
=
∑
d
=
1
N
μ
(
d
)
f
(
⌊
n
d
⌋
)
f
(
⌊
m
d
⌋
)
=\sum_{d=1}^N\mu(d) f(\left \lfloor \frac{n}{d} \right \rfloor)f(\left \lfloor \frac{m}{d} \right \rfloor)
=d=1∑Nμ(d)f(⌊dn⌋)f(⌊dm⌋)
就可以在
O
(
n
+
m
)
O(\sqrt{n}+\sqrt{m})
O(n+m)的复杂度下求解了
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int d[N];
int prime[N];
int vis[N];
int num[N];
int mu[N];
int cnt;
void init()
{
cnt=0;
d[1]=1;
mu[1]=1;
for(int i=2;i<N;i++)
{
if(!vis[i])
{
prime[cnt++]=i;
d[i]=2;
num[i]=1;
mu[i]=-1;
}
for(int j=0;prime[j]*i<N&&j<cnt;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]!=0)
{
d[i*prime[j]]=d[i]*2;
num[i*prime[j]]=1;
mu[i*prime[j]]=-mu[i];
}
else
{
d[i*prime[j]]=d[i]/(num[i]+1)*(num[i]+2);
num[i*prime[j]]=num[i]+1;
mu[i*prime[j]]=0;
break;
}
}
}
for(int i=1;i<N;i++)
{
d[i]=d[i-1]+d[i];
mu[i]=mu[i-1]+mu[i];
}
}
int main()
{
init();
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
long long sum=0;
int minn=min(n,m);
for(int i=1,last;i<=minn;i=last+1)
{
last=min(n/(n/i),m/(m/i));
sum+=1ll*(mu[last]-mu[i-1])*d[n/i]*d[m/i];
}
printf("%lld\n",sum);
}
return 0;
}