题意
传送门 P2257 YY的GCD
题解
求
∑
p
m
i
n
(
n
,
m
)
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
p
]
\sum\limits_{p}^{min(n,m)}\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[gcd(i,j)=p]
p∑min(n,m)i=1∑nj=1∑m[gcd(i,j)=p],不易直接求解,考虑
d
∣
g
c
d
(
i
,
j
)
d|gcd(i,j)
d∣gcd(i,j) 的关系,设
F
(
d
)
=
∑
d
∣
k
f
(
k
)
F(d)=\sum\limits_{d|k}f(k)
F(d)=d∣k∑f(k) 莫比乌斯反演,得到
f
(
p
)
=
∑
p
∣
d
μ
(
d
p
)
F
(
d
)
f(p)=\sum\limits_{p|d}\mu(\frac{d}{p})F(d)
f(p)=p∣d∑μ(pd)F(d)
则答案为
∑
p
m
i
n
(
n
,
m
)
∑
p
∣
d
μ
(
d
p
)
⌊
n
d
⌋
⌊
m
d
⌋
\sum\limits_{p}^{min(n,m)}\sum\limits_{p|d}\mu(\frac{d}{p})\lfloor \frac{n}{d}\rfloor \lfloor \frac{m}{d} \rfloor
p∑min(n,m)p∣d∑μ(pd)⌊dn⌋⌊dm⌋ 直接枚举素数加整除分块复杂度为
O
(
T
n
×
n
log
n
)
O(T\sqrt n\times \frac{n}{\log n})
O(Tn×lognn),考虑优化,交换求和次序
∑
d
=
1
m
i
n
(
n
,
m
)
⌊
n
d
⌋
⌊
m
d
⌋
∑
p
∣
d
μ
(
d
p
)
\sum\limits_{d=1}^{min(n,m)}\lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor\sum\limits_{p|d} \mu(\frac{d}{p})
d=1∑min(n,m)⌊dn⌋⌊dm⌋p∣d∑μ(pd) 考虑维护最后一项的前缀和。线性筛求莫比乌斯函数,然后枚举素数
O
(
n
log
n
)
O(\frac{n}{\log n})
O(lognn),更新其倍数,均摊
O
(
log
n
)
O(\log n)
O(logn)。总时间复杂度
O
(
n
+
T
n
)
O(n+T\sqrt n)
O(n+Tn)。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 10000005
typedef long long ll;
int prime[maxn >> 1], mu[maxn], _sum[maxn], sum[maxn];
bool used[maxn];
int sieve()
{
int p = 0;
mu[1] = 1;
for (int i = 2; i < maxn; ++i)
{
if (!used[i])
{
prime[p++] = i;
mu[i] = -1;
}
for (int j = 0; j < p && i * prime[j] < maxn; ++j)
{
int k = i * prime[j];
used[k] = 1;
if (i % prime[j] == 0)
{
mu[k] = 0;
break;
}
else
mu[k] = -mu[i];
}
}
return p;
}
void init()
{
int p = sieve();
for (int i = 0; i < p; ++i)
for (int j = prime[i], k = 1; j < maxn; j += prime[i], ++k)
_sum[j] += mu[k];
for (int i = 1; i < maxn; ++i)
sum[i] = sum[i - 1] + _sum[i];
}
ll count(int n, int m)
{
ll res = 0;
if (n > m)
swap(n, m);
for (int i = 1, last; i <= n; i = last + 1)
{
last = min(n / (n / i), m / (m / i));
res += 1LL * (n / i) * (m / i) * (sum[last] - sum[i - 1]);
}
return res;
}
int main()
{
init();
int t, n, m;
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &n, &m);
printf("%lld\n", count(n, m));
}
return 0;
}