洛谷 P3455 [POI2007]ZAP-Queries
题意
有\(T\)组询问,每次询问求
\[\sum\limits_{x=1}^{a}\sum\limits_{y=1}^{b}[\gcd(x,y)=d]
\]
思路
因为我不喜欢用\(x、y、a、b、d\),所以一一对应换成\(i、j、n、m、k\)
直接淦式子
\[\begin{align*}&\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=k]\\=& \sum\limits_{i=1}^{\lfloor \frac nk\rfloor}\sum\limits_{j=1}^{\lfloor\frac mk\rfloor}[\gcd(i,j)=1]\\=&\sum\limits_{i=1}^{\lfloor \frac nk\rfloor}\sum\limits_{j=1}^{\lfloor\frac mk\rfloor}\sum\limits_{d|\gcd(i,j)}\mu(d)\\=&\sum\limits_{i=1}^{\lfloor \frac nk\rfloor}\sum\limits_{j=1}^{\lfloor\frac mk\rfloor}\sum\limits_{d|i}\sum _{d|j}\mu(d)\\=&\sum\limits_{d=1}^{\min(\lfloor\frac nk\rfloor,\lfloor\frac mk\rfloor)}\mu(d)\sum\limits_{i=1}^{\lfloor \frac nk\rfloor}\sum\limits_{j=1}^{\lfloor\frac mk\rfloor}\sum\limits_{d|i}\sum\limits_{d|j}1\\=&\sum\limits_{d=1}^{\min(\lfloor\frac nk\rfloor,\lfloor\frac mk\rfloor)}\mu(d)\sum\limits_{i=1}^{\lfloor \frac nk\rfloor}\sum\limits_{d|i}1\sum\limits_{j=1}^{\lfloor\frac mk\rfloor}\sum\limits_{d|j}1\\=&\sum\limits_{d=1}^{\min(\lfloor\frac nk\rfloor,\lfloor\frac mk\rfloor)}\mu(d)\lfloor\frac{\lfloor \frac nk\rfloor}d\rfloor\lfloor\lfloor\frac{\frac mk\rfloor}d\rfloor\\=&\sum\limits_{d=1}^{\min(\lfloor\frac nk\rfloor,\lfloor\frac mk\rfloor)}\mu(d)\lfloor\frac n{kd}\rfloor\lfloor\frac m{kd}\rfloor\end{align*}
\]
现在就可以每次询问\(O(n)\)做这道题了
但是跑不过啊,不过显然可以数论分块,所以我们就可以\(O(\sqrt n)\)回答每次询问了
代码
/*
Author:loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int A = 5e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline int read() {
char c = getchar(); int x = 0, f = 1;
for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
int n, m, k, mu[A], p[A], sum[A], cnt;
bool vis[A];
void getmu(int n) {
mu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) p[++cnt] = i, mu[i] = -1;
for (int j = 1; j <= cnt && i * p[j] <= n; j++) {
vis[i * p[j]] = 1;
if (i % p[j] == 0) break;
mu[i * p[j]] -= mu[i];
}
}
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i];
}
int solve(int n, int m, int k) {
int ans = 0, maxn = min(n, m);
for (int l = 1, r; l <= maxn; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans += (sum[r] - sum[l - 1]) * (n / (k * l)) * (m / (k * l));
}
return ans;
}
int main() {
getmu(50000);
int T = read();
while (T--) {
n = read(), m = read(), k = read();
cout << solve(n, m, k) << '\n';
}
return 0;
}
转载不必联系作者,但请声明出处