洛谷 P3455

题目链接:P3455 [POI2007]ZAP-Queries

题目大意

\(\sum\limits_{i = 1}^{n}\sum\limits_{j = 1}^{m}gcd(i, j) = d\)

solution

又是莫比乌斯反演的板子题, 现在让我们来推一下具体的过程

\(f(k) = \sum\limits_{i = 1}^{n}\sum\limits_{j = 1}^{m}[\gcd(i,j) == k]\)

\(\Rightarrow f(k) = \sum\limits_{i = 1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum\limits_{j = 1}^{\left\lfloor\frac{m}{k}\right\rfloor}[\gcd(i,j) == 1]\)

\(\Rightarrow f(k) = \sum\limits_{i = 1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum\limits_{j = 1}^{\left\lfloor\frac{m}{k}\right\rfloor}\sum\limits_{d|\gcd(i,j)}\mu(d)\)

\(\Rightarrow f(k) = \sum\limits_{i = 1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum\limits_{j = 1}^{\left\lfloor\frac{m}{k}\right\rfloor}\sum\limits_{d|i}\sum\limits_{d|j}\mu(d)\)

\(\Rightarrow f(k) = \sum\limits_{d = 1}^{\left\lfloor\frac{min(n, m)}{k}\right\rfloor}\mu(d)\sum\limits_{i = 1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum\limits_{d|i}\sum\limits_{j = 1}^{\left\lfloor\frac{m}{k}\right\rfloor}\sum\limits_{d|j}1\)

\(\Rightarrow f(k) = \sum\limits_{d = 1}^{\left\lfloor\frac{min(n, m)}{k}\right\rfloor}\mu(d)\left\lfloor\frac{n}{kd}\right\rfloor\left\lfloor\frac{m}{kd}\right\rfloor\)

然后我们用一下整数分块,求出每一个 \(f(k) \times k\) 加起来就是 \(\sum\limits_{i = 1}^{n}\sum\limits_{j = 1}^{m}gcd(i, j)\)
然后我们有线性筛筛欧拉函数就好了

Code:

/**
*    Author: Alieme
*    Data: 2020.9.7
*    Problem: Luogu P3455
*    Time: O()
*/
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>

#define int long long
#define rr register

#define inf 1e9
#define MAXN 100010

using namespace std;

inline int read() {
	int s = 0, f = 0;
	char ch = getchar();
	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

void print(int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) print(x / 10);
	putchar(x % 10 + 48);
}

int T, n, m, k, tot, ans;

int sum[MAXN], mu[MAXN], prime[MAXN];

bool vis[MAXN];

inline void init() {
	mu[1] = 1;
	for (rr int i = 2; i <= 100000; i++) {
		if (!vis[i]) prime[++tot] = i, mu[i] = -1;
		for (rr int j = 1; j <= tot; j++) {
			if (i * prime[j] > 100000) break;
			mu[i * prime[j]] = -mu[i];
			vis[i * prime[j]] = 1;
			if (i % prime[j] == 0) {
				mu[i * prime[j]] = 0;
				break;
			}
		}
	}

	for (rr int i = 1; i <= 100000; i++) sum[i] = sum[i - 1] + mu[i];
}

signed main() {
	init();
	T = read();
	while (T--) {
		n = read();
		m = read();
		k = read();
		n /= k,	m /= k;
		ans = 0;	
		for (rr int l = 1, r; l <= min(n, m); l = r + 1) {
			r = min(n / (n / l), m / (m / l));
			ans += (sum[r] - sum[l - 1]) * (n / l) * (m / l);
		}
		print(ans);
		puts("");
	}
}
posted @ 2020-09-07 21:16  Aliemo  阅读(105)  评论(0编辑  收藏  举报