BZOJ2671 Calc <莫比乌斯反演>

Problem

Calc

Time  Limit:  10  Sec\mathrm{Time\;Limit:\;10\;Sec}
Memory  Limit:  128  MB\mathrm{Memory\;Limit:\;128\;MB}

Description

给出NN,统计满足下面条件的数对(a,b)(a,b)的个数:

  • 1a<bN1\le a<b\le N
  • (a+b)(ab)(a+b)|(a\cdot b)

Input

一行一个数NN

Output

一行一个数表示答案。

Sample Input

1
15

Sample Output

1
4

HINT

测试点编号</font color=#000000> 数据规模</font color=#000000> 测试点编号</font color=#000000> 数据规模</font color=#000000>
0101 N10N\le 10 1111 N5×107N\le 5\times 10^7
0202 N50N \le 50 1212 N108N\le 10^8
0303 N103N\le 10^3 1313 N2×108N\le 2\times 10^8
0404 N5×103N\le 5\times 10^3 1414 N3×108N\le 3\times 10^8
0505 N2×104N\le 2\times 10^4 1515 N5×108N\le 5\times 10^8
0606 N2×105N\le 2\times 10^5 1616 N109N\le 10^9
0707 N2×106N\le 2\times 10^6 1717 N109N\le 10^9
0808 N107N\le 10^7 1818 N2311N\le 2^{31}-1
0909 N2×107N\le 2\times 10^7 1919 N2311N\le 2^{31}-1
1010 N3×107N\le 3\times 10^7 2020 N2311N\le 2^{31}-1

标签:莫比乌斯反演

Solution

一道稍有变形的莫比乌斯反演,blutrexblutrexO(n34)O(n^{\frac{3}{4}})的算法,但我只会小常数的O(n)O(n)算法,不过可以过BZOJBZOJ数据。

问题即求i=1nj=i+1n[(i+j)(i×j)]\sum_{i=1}^{n}\sum_{j=i+1}^{n}{[(i+j)|(i\times j)]}的值。设gcd(i,j)=d\gcd(i,j)=d, i=x×di=x\times d, j=y×dj=y\times d,易知gcd(x,y)=1\gcd(x,y)=1
那么(i+j)(i×j)(i+j)|(i\times j)    \iff((x+y)×d)(x×y×d2)((x+y)\times d)|(x\times y\times d^2)    \iff(x+y)(x×y×d)(x+y)|(x\times y\times d)    \iff(x+y)d(x+y)|d
不妨设d=k×(x+y)d=k\times(x+y),那么a=x×d=k×x×(x+y)a=x\times d=k\times x\times(x+y), b=y×d=k×y×(x+y)b=y\times d=k\times y\times(x+y)

\begin{equation} \begin{aligned} 原式 =& \sum_{x=1}^{n}\sum_{y=x+1}^{n}[\gcd(x,y)=1\ \&\&\ a<b\le n]\\ =& \sum_{y=1}^{\lfloor\sqrt{n}-1\rfloor}\sum_{x=1}^{y-1}\sum_{k\in \mathbb{Z}^*}{[\gcd(x,y)=1\ \&\&\ k\times y\times (x+y)\le n]}\\ =& \sum_{y=1}^{\lfloor\sqrt{n}-1\rfloor}\sum_{x=1}^{y-1}[\gcd(x,y)=1]\times \lfloor\frac{n}{y\times(x+y)}\rfloor\\ \end{aligned} \end{equation}

显然val=ny×(x+y)val=\lfloor\frac{n}{y\times(x+y)}\rfloor只有n\sqrt{n}级别种取值,可以根号分块来算,即枚举yy,每次找到valval相等的一段x[lo,hi]x\in[lo,hi],统计[lo,hi][lo,hi]间满足gcd(x,y)=1\gcd(x,y)=1xx的个数,可以套用基础莫比乌斯反演公式,即dyμ(d)(hidlo1d)\sum_{d|y}{\mu(d)\cdot(\lfloor\frac{hi}{d}\rfloor-\lfloor\frac{lo-1}{d}\rfloor)}

此算法先枚举yy的取值,再枚举valval的取值,最后枚举yy的约数dd计算反演。其中yyn\sqrt{n}级别种取值,valval所对应的[lo,hi][lo,hi]都在(0,y)(0,y)之间,即共有yn4\sqrt{y}\thickapprox\sqrt[4]{n}级别种取值,而最后的dd又有yn4\sqrt{y}\thickapprox\sqrt[4]{n}级别种取值,故总时间复杂度应为O(n)O(n)。但是由于valval的取值总数通常到不了n4\sqrt[4]{n}级别,且dd的取值总数通常也到不了n4\sqrt[4]{n}级别,因此常数非常小,跑得贼快,可以过此题2×1092\times 10^9级别的数据。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <bits/stdc++.h>
#define MAX_N 100000
using namespace std;
typedef long long lnt;
template <class T>
inline void read(T &x) {
x = 0; int c = getchar(), f = 1;
for (; !isdigit(c); c = getchar()) if (c == 45) f = -1;
for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0');
}
int pri[MAX_N+5], mu[MAX_N+5], fac[MAX_N+5], cnt; bool NotPri[MAX_N+5];
void PriS() {
mu[1] = 1;
for (int i = 2; i <= MAX_N; i++) {
if (!NotPri[i]) pri[cnt++] = i, mu[i] = -1;
for (int j = 0, x; j < cnt; j++) {
if ((x = i*pri[j]) > MAX_N) break;
NotPri[x] = true;
if (i%pri[j]) mu[x] = -mu[i];
else {mu[x] = 0; break;}
}
}
}
int main() {
int n; read(n), PriS(); lnt ans = 0LL;
for (lnt i = 1, l, r; i < sqrt(n); i++) {
cnt = 0; for (int j = 1; j <= sqrt(i); j++) if (i%j == 0) fac[cnt++] = j;
for (l = 1; l < i; l = r+1) {
lnt val = n/(i*(i+l)); if (!val) break; r = min(n/val/i-i, i-1);
for (lnt k = 0, j; k < cnt; k++) {
j = fac[k], ans += mu[j]*(r/j-(l-1)/j)*val; lnt t = i/j;
if (i%t == 0 && (j^t)) ans += mu[t]*(r/t-(l-1)/t)*val;
}
}
}
return printf("%lld\n", ans), 0;
}