bzoj 3529 [Sdoi2014]数表 莫比乌斯反演+树状数组+离线处理

题面

题目传送门

解法

  • 显然,对于每一个格子 ( i , j ) (i,j) ,在不管限制的情况下它对答案的贡献为 σ ( g c d ( i , j ) ) \sigma(gcd(i,j)) σ ( i ) \sigma(i) 表示 i i 的约数和)。
  • 那么我们不妨考虑每一个 d = g c d ( i , j ) d=gcd(i,j) 对整个答案的贡献,应该是 σ ( d ) i = 1 n j = 1 m [ g c d ( i , j ) = = d ] \sigma(d)\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==d]
  • 反演一下就可以得到 k = 1 n n k m k d k μ ( k d ) σ ( d ) \sum_{k=1}^n\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor\sum_{d|k}\mu(\frac{k}{d})\sigma(d)
  • 然后再重新考虑一下询问中 a a 的限制,我们考虑把询问离线下来按照 a a 的大小排序,同时也把所有数按照 σ ( i ) \sigma(i) 排序。
  • 那么我们考虑在把 i i 一个一个加进来的时候怎么维护答案。令 f ( i ) = d i μ ( i d ) σ ( d ) f(i)=\sum_{d|i}\mu(\frac{i}{d})\sigma(d) ,那么我们只要维护出 f ( i ) f(i) 的前缀和就可以了。维护的话直接暴力用 O ( log 2 n ) O(\log^2n) 的复杂度更新,这个用树状数组实现即可。
  • 时间复杂度: O ( q ( n log n + log 2 n ) ) O(q(\sqrt n\log n+\log^2n))
  • 因为是对 2 31 2^{31} 取模,所以直接自然溢出就可以了。

代码

#include <bits/stdc++.h>
#define N 100010
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
	int x, id;
	bool operator < (const Node &a) const {return x < a.x;}
} a[N];
struct Que {
	int n, m, s, id;
	bool operator < (const Que &b) const {
		if (s != b.s) return s < b.s;
		return n < b.n;
	}
} b[N];
struct BIT {
	int f[N];
	int lowbit(int x) {return x & -x;}
	void modify(int x, int v) {
		for (; x <= 1e5; x += lowbit(x)) f[x] += v;
	}
	int query(int x) {
		int ret = 0;
		for (; x; x -= lowbit(x)) ret += f[x];
		return ret;
	}
} T;
bool f[N]; int p[N], s[N], mu[N], ans[N];
void sieve(int n) {
	mu[1] = 1; int len = 0;
	memset(f, true, sizeof(f));
	for (int i = 1; i <= n; i++)
		for (int j = i; j <= n; j += i)
			s[j] += i;
	for (int i = 2; i <= n; i++) {
		if (f[i]) p[++len] = i, mu[i] = -1;
		for (int j = 1; j <= len && i * p[j] <= n; j++) {
			int k = i * p[j]; f[k] = false;
			if (i % p[j] == 0) {mu[k] = 0; break;}
			mu[k] = -mu[i];
		}
	}
	for (int i = 1; i <= n; i++) a[i] = (Node) {s[i], i};
	sort(a + 1, a + n + 1);
}
int solve(int n, int m) {
	int ret = 0, x = 0;
	for (int i = 1; i <= n; i = x + 1) {
		x = min(n / (n / i), m / (m / i));
		ret += (T.query(x) - T.query(i - 1)) * (n / i) * (m / i);
	}
	return ret;
}
int main() {
	sieve(1e5); int q; read(q);
	for (int i = 1; i <= q; i++) {
		int n, m, s; read(n), read(m), read(s);
		if (n > m) swap(n, m);
		b[i] = (Que) {n, m, s, i};
	}
	sort(b + 1, b + q + 1);
	int sum = 0, l = 1;
	for (int i = 1; i <= q; i++) {
		while (l <= 1e5 && a[l].x <= b[i].s) {
			int x = a[l++].id;
			for (int j = x; j <= 1e5; j += x)
				T.modify(j, s[x] * mu[j / x]);
		}
		ans[b[i].id] = solve(b[i].n, b[i].m);
		if (ans[b[i].id] < 0) ans[b[i].id] += 1ll << 31;
	}
	for (int i = 1; i <= q; i++) cout << ans[i] << "\n";
	return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/84473154
今日推荐