「LibreOJ NOI Round #1」动态几何问题(mobius反演+分块)
题目相当于求:
\(\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);
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址