Problem
Calc
TimeLimit:10Sec
MemoryLimit:128MB
Description
给出N,统计满足下面条件的数对(a,b)的个数:
- 1≤a<b≤N
- (a+b)∣(a⋅b)
一行一个数N。
Output
一行一个数表示答案。
Sample Output
HINT
测试点编号</font color=#000000> |
数据规模</font color=#000000> |
测试点编号</font color=#000000> |
数据规模</font color=#000000> |
01 |
N≤10 |
11 |
N≤5×107 |
02 |
N≤50 |
12 |
N≤108 |
03 |
N≤103 |
13 |
N≤2×108 |
04 |
N≤5×103 |
14 |
N≤3×108 |
05 |
N≤2×104 |
15 |
N≤5×108 |
06 |
N≤2×105 |
16 |
N≤109 |
07 |
N≤2×106 |
17 |
N≤109 |
08 |
N≤107 |
18 |
N≤231−1 |
09 |
N≤2×107 |
19 |
N≤231−1 |
10 |
N≤3×107 |
20 |
N≤231−1 |
标签:莫比乌斯反演
Solution
一道稍有变形的莫比乌斯反演,blutrex有O(n43)的算法,但我只会小常数的O(n)算法,不过可以过BZOJ数据。
问题即求∑i=1n∑j=i+1n[(i+j)∣(i×j)]的值。设gcd(i,j)=d, i=x×d, j=y×d,易知gcd(x,y)=1。
那么(i+j)∣(i×j)⟺((x+y)×d)∣(x×y×d2)⟺(x+y)∣(x×y×d)⟺(x+y)∣d。
不妨设d=k×(x+y),那么a=x×d=k×x×(x+y), b=y×d=k×y×(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=⌊y×(x+y)n⌋只有n级别种取值,可以根号分块来算,即枚举y,每次找到val相等的一段x∈[lo,hi],统计[lo,hi]间满足gcd(x,y)=1的x的个数,可以套用基础莫比乌斯反演公式,即∑d∣yμ(d)⋅(⌊dhi⌋−⌊dlo−1⌋)。
此算法先枚举y的取值,再枚举val的取值,最后枚举y的约数d计算反演。其中y有n级别种取值,val所对应的[lo,hi]都在(0,y)之间,即共有y≈4n级别种取值,而最后的d又有y≈4n级别种取值,故总时间复杂度应为O(n)。但是由于val的取值总数通常到不了4n级别,且d的取值总数通常也到不了4n级别,因此常数非常小,跑得贼快,可以过此题2×109级别的数据。
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; }
|