「LibreOJ NOI Round #1」动态几何问题(mobius反演+分块)

https://loj.ac/problem/509

题目相当于求:
\(\sum_{a=1}^n\sum_{n=1}^m [ab是完全平方数]\)

如何统计,考虑枚举\(a,b\)的共有的指数为奇数的质因子的乘积\(d\)

\(\sum_{d=1}^n \mu^2(d) \sqrt{n/d}*\sqrt{m/d}\)

\(\sqrt{n/d}*\sqrt{m/d}\)分块,现在要求\(\mu^2\)的前缀和

\(\sum_{i=1}^n \mu^2(i) = \sum_{i=1}^{\sqrt n} \mu(i)*(n/i/i)\)
证明就是\(\sum_{d|n}\mu(d)=[n=1]\)

预处理\(\mu\)的前缀和之后,这个同样可以分块求,\(n/i/i\)只有\(n^{1/3}\)种不同取值。

这样有95分,考虑再处理\(\sqrt n内的\mu^2\)的前缀和,就可以100分了,注意空间问题

据官方题解的积分,复杂度是\(O(\sqrt n)\)

复杂度瓶颈在预处理,考虑\(\mu\)不预处理那么多,剩下的一点用杜教筛求,这样可以更快,不写了。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 122474500;

int mu[N], s[N];
int p[7000000], p0;

ll n, m, sq;

void sieve(int n) {
	fo(i, 2, n) {
		if(!s[i]) p[++ p0] = i, mu[i] = -1;
		for(int j = 1; i * p[j] <= n; j ++) {
			int k = i * p[j];
			s[k] = 1;
			if(i % p[j] == 0) { mu[k] = 0; break;}
			mu[k] = -mu[i];
		}
	}
	mu[1] = 1;
	fo(i, 1, n) s[i] = s[i - 1] + (mu[i] != 0), mu[i] += mu[i - 1];
}

int cc = 0;

ll calc(ll n) {
	if(n <= sq) return s[n];
	int m = sqrt(n);
	ll s = 0;
	for(int i = 1, j; i <= m; i = j + 1) {
		ll x = n / i / i;
		j = sqrt(n / x);
		s += (mu[j] - mu[i - 1]) * x;
	}
	return s;
}

int main() {
	scanf("%lld %lld", &n, &m);
	if(n > m) swap(n, m);
	sq = sqrt(n);
	sieve(sq);
	ll v1 = 0, ans = 0;
	for(ll i = 1, j; i <= n; i = j + 1) {
		ll x = sqrt(n / i), y = sqrt(m / i);
		j = n / x / x;
		j = min(j, m / y / y);
		ll v2 = calc(j);
		ans += (v2 - v1) * x * y;
		v1 = v2;
	}
	pp("%lld\n", ans);
}
posted @ 2020-04-09 21:54  Cold_Chair  阅读(224)  评论(0编辑  收藏  举报