https://www.luogu.org/problemnew/show/P2257
一年前留下的莫比乌斯反演的坑,竟发现一年后还是不懂(这不是废话嘛)!
但是我觉得搞一搞还是可以的。
实际上算是第一次做莫比乌斯反演问题。
初步感觉解决这类问题的办法就是推公式,当然不是说是这么简单,日后发现有问题还将更改。
一般来说将一个枚举求和问题转换成一个整数分块求前缀和的问题。 F F 与的关系使得这种变化成为了可能,也就是一开始的默比乌斯反演公式。
最终化简结果:
Ans=∑T=1min(n,m)⌊nT⌋⌊mT⌋(∑t|Tμ(⌊Tt⌋))
A
n
s
=
∑
T
=
1
m
i
n
(
n
,
m
)
⌊
n
T
⌋
⌊
m
T
⌋
(
∑
t
|
T
μ
(
⌊
T
t
⌋
)
)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<queue>
#include<vector>
using namespace std;
#define ll long long int
#define INF 0x3f3f3f3f
const int maxn = 1e7 + 10;
int n, m;
bool vis[maxn];
ll sum[maxn];
int prim[maxn];
int mu[maxn], g[maxn];
int cnt;
void get_mu(int n) {
mu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) { mu[i] = -1; prim[++cnt] = i; }
for (int j = 1; j <= cnt && prim[j] * i <= n; j++) {
vis[i*prim[j]] = 1;
if (i%prim[j] == 0) break;
else mu[prim[j] * i] = -mu[i];
}
}
for (int j = 1; j <= cnt; j++)
for (int i = 1; i*prim[j] <= n; i++)
g[i*prim[j]] += mu[i];
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + 1ll * g[i];
}
int main()
{
int T;
scanf("%d", &T);
get_mu(10000000);
while (T--) {
scanf("%d %d", &n, &m);
if (n > m) swap(n, m);
ll ans = 0;
for (int l = 1, r; l <= n; l = r + 1)
{
r = min(n / (n / l), m / (m / l));
ans += 1ll * (n / l)*(m / l)*(sum[r] - sum[l - 1]);
}
printf("%lld\n", ans);
}
return 0;
}
//_CRT_SECURE_NO_WARNINGS