题目描述
FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d。作为FGD的同学,FGD希望得到你的帮助。
分析
很明显的一道莫比乌斯反演,但是还没有写过学习笔记,之后一定补起来(flag)。
\[f(k)=\sum^a_{i=1}\sum^b_{j=1}[gcd(i,j)=k]\]
\[F(k) = \sum_{n|k}f(k)= \lfloor \frac{a}{n} \rfloor \lfloor \frac{b}{n} \rfloor \]
由反演退出以下的式子:
\[f(n) = \sum_{n|k} \mu (\lfloor \frac{k}{n}\rfloor) F(k)\]
那么答案就是\(f(d)\),
我们枚举整除分块$\lfloor \frac k d \rfloor $
递推式就是:
\[ans=\sum^{min(a,b)}_{t=1} \mu(t) \lfloor \frac{a}{td} \rfloor \lfloor \frac{b}{td} \rfloor \]
多组数据我们就用整除分块,差不多复杂度是\(O(t\sqrt{n})\)
ps.记得要开long long。
ac代码
#include <bits/stdc++.h>
#define ll long long
#define ms(a, b) memset(a, b, sizeof(a))
#define inf 0x3f3f3f3f
using namespace std;
template <typename T>
inline void read(T &x) {
x = 0; T fl = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
if (ch == '-') fl = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
x *= fl;
}
#define N 500005
ll sum[N], mui[N], prime[N];
int cnt;
bool vis[N];
void get_mui(ll MAXN) {
mui[1] = 1;
for (ll i = 2; i <= MAXN; i ++) {
if (!vis[i]) {
mui[i] = -1;
prime[++ cnt] = i;
}
for (ll j = 1; j <= cnt && prime[j] * i <= MAXN; j ++) {
vis[prime[j] * i] = 1;
if (i % prime[j] == 0) break;
else mui[prime[j] * i] = -mui[i];
}
}
for (ll i = 1; i <= MAXN; i ++) sum[i] = sum[i - 1] + mui[i];
}
int main() {
int cas;
read(cas);
get_mui(500000);
while (cas --) {
ll a, b, d;
read(a); read(b); read(d);
ll ans = 0;
for (ll l = 1, r; l <= min(a, b); l = r + 1) {
r = min(a / (a / l), b / (b / l));
ans += (a / (l * d) * (b / (l * d))) * (sum[r] - sum[l - 1]);
}
printf("%lld\n", ans);
}
return 0;
}