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

https://loj.ac/problem/509

题目相当于求:
(sum_{a=1}^nsum_{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("
")
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
", ans);
}
原文地址:https://www.cnblogs.com/coldchair/p/12669862.html