题目:
预备知识:莫比乌斯定理(懵逼乌斯定理)
μ∗1=ϵ
μ
∗
1
=
ϵ
(证bu明hui略zheng)
其中(我校学长把
ϵ(x)
ϵ
(
x
)
叫单位函数但是为什么我没百度到qwq)
那个 ∗ ∗ 是迪利克雷卷积,换成人话就是
分析:
这题莫比乌斯定理的经典用例。
本文中默认
N>M
N
>
M
默认
p
p
是质数
显然如果,那么
gcd(ip,jp)=1
g
c
d
(
i
p
,
j
p
)
=
1
那么题目所求可以转换成下面的式子
其中(我校学长把这个叫单位函数但是我没百度到qwq)
根据莫比乌斯反演定理,上面的式子就可以变成
改变一下枚举顺序,用 d⋅i d · i 表示原来的 i i ,表示原来的 j j ,得到
可以发现 μ(d) μ ( d ) 和 i i 、没半毛钱关系,仅仅是乘上 i i 和可以取的值的数量
也就是
令 T=pd T = p d ,枚举T,上式可变成
设
则上式就是
现在考虑如何求 g(x) g ( x ) 这个函数。
首先,对于任意质数 p p ,显然
然后,对于任意合数 n=kp0 n = k p 0 ( p0 p 0 是质数) g(n) g ( n ) 中显然存在 μ(np0) μ ( n p 0 ) 也就是 μ(k) μ ( k ) 这一项
当 p0|k p 0 | k ,也就是 p20|n p 0 2 | n ,对于任意 p|k p | k 且 p≠p0 p ≠ p 0 , μ(np) μ ( n p ) 中一定有 p20 p 0 2 这个质数平方因子。根据 μ(x) μ ( x ) 的定义, μ(np)=0 μ ( n p ) = 0
所以此时 g(n)=μ(k) g ( n ) = μ ( k )
当 p0 p 0 不能整除 k k ,对于任意, μ(np) μ ( n p ) 比 μ(kp) μ ( k p ) 多了 p0 p 0 这个质因子。根据 μ(x) μ ( x ) 的定义 μ(np)=−μ(kp) μ ( n p ) = − μ ( k p )
所以此时 g(n)=−g(k)+μ(k) g ( n ) = − g ( k ) + μ ( k )
总结一下
显然这个函数可以用线性筛求
再来看这个式子,既然 g(T) g ( T ) 可以直接预处理并 O(1) O ( 1 ) 查询,那么计算这个式子的时间复杂度就是枚举 T T 的复杂度
我会做啦!
别急,这题还有
T
T
组询问,所以复杂度是O(不可过),这个过不了。
但是我们可以发现
⌊NT⌋∗⌊MT⌋
⌊
N
T
⌋
∗
⌊
M
T
⌋
在
T
T
的一段区间内是不变的,所以可以给算个前缀和然后分段计算,据说复杂度是
O(N−−√T)
O
(
N
T
)
的(我不会证),这样就可以过了
代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
namespace zyt
{
typedef long long ll;
const int N = 1e7 + 10, M = 7e5;
bool mark[N];
int cnt, prime[M], phi[N], mu[N];
ll g[N];
void init()
{
mu[1] = 1;
for (int i = 2; i < N; i++)
{
if (!mark[i])
prime[cnt++] = i, mu[i] = -1, g[i] = 1;
for (int j = 0; j < cnt && (ll)i * prime[j] < N; j++)
{
int k = i * prime[j];
mark[k] = true;
if (i % prime[j] == 0)
{
mu[k] = 0;
g[k] = mu[i];
break;
}
else
{
mu[k] = -mu[i];
g[k] = -g[i] + mu[i];
}
}
}
for (int i = 2; i < N; i++)
g[i] += g[i - 1];
}
void work()
{
int T;
init();
scanf("%d", &T);
while (T--)
{
int n, m, pos = cnt;
ll ans = 0;
scanf("%d%d", &n, &m);
if (n > m)
swap(n, m);
for (int t = 1; t <= n;)
{
int tmp = min(n / (n / t), m / (m / t));
ans += (g[tmp] - g[t - 1]) * (n / t) * (m / t);
t = tmp + 1;
}
printf("%lld\n", ans);
}
}
}
int main()
{
zyt::work();
return 0;
}