#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, ans = 0;
cin >> n;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
int p = i, q = j;
int g = __gcd(p, q);
p /= g; q /= g;
while(q % 2 == 0) q /= 2;
while(q % 5 == 0) q /= 5;
if(q == 1) ans++;
}
}
cout << ans << endl;
return 0;
}
做法 2
把分数分解一下,可以写为以下的形式:
\frac{bc}{ac}
那么,依次在 $[1, n]$ 范围枚举 $a$(合法的 $a$ 的表可以预处理)在 $[1, \lfloor\frac{n}{a} \rfloor]$ 范围枚举合法的 $c$,在 $[1, \lfloor\frac{n}{c} \rfloor]$ 的范围内枚举 $b$,每枚举到一次就把答案加 $1$,时间复杂度 $O(n^2)$。
```cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, x[100000], cnt, ans;
signed main()
{
cin >> n;
for(int i = 1; i <= n; i *= 2)
for(int j = i; j <= n; j *= 5)
x[++cnt] = j;
sort(x + 1, x + cnt + 1);
for(int i = 1; i <= cnt; i++)
{
int a = x[i];
for(int b = 1; a * b <= n; b++)
{
if(b % 2 == 0 || b % 5 == 0) continue;
for(int c = 1; b * c <= n; c++)
{
ans++;
}
}
}
cout << ans << endl;
return 0;
}
```
## 子任务 2:$n \le 10^7
被极短的 O(n \log n) 切了,长见识了 qwq
做法 1
观察上一个做法,发现不知道最后一重循环在干啥 /发抖
也就是说,枚举完一组 a 和 c 后,它对答案的贡献就是分子所能取的数量,即 [1, \lfloor\frac{n}{c} \rfloor],直接加上这个贡献就好了。
那么省掉了一重循环,因为分母的枚举是 n 个不重不漏,时间复杂度 O(n)。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, x[100000], cnt, ans;
signed main()
{
cin >> n;
for(int i = 1; i <= n; i *= 2)
for(int j = i; j <= n; j *= 5)
x[++cnt] = j;
sort(x + 1, x + cnt + 1);
for(int i = 1; i <= cnt; i++)
{
int a = x[i];
for(int b = 1; a * b <= n; b++)
{
if(b % 2 == 0 || b % 5 == 0) continue;
ans += n / b;
}
}
cout << ans << endl;
return 0;
}
做法 2
想要继续优化,发现很难了,回到 O(n^2 ) 的做法,能不能通过转换顺序来达到优化时间的目的呢?
\frac{bc}{ac}
尝试先枚举 c,那么,b 的取值依然是 [1, \lfloor\frac{n}{c} \rfloor] 的任何数,而 a 的取值就变为了 [1, \lfloor\frac{n}{c} \rfloor] 中任何只含2, 5 两种因子的数,发现,a 的取值的个数随着 c 的增大而单调减小,所以可以在总共 O(n) 的时间中计算,也就是,对于每个 c,记合法的 a 的个数为 d,则它对答案的贡献为 d \times \lfloor\frac{n}{c} \rfloor。
时间复杂度依然是 O(n)。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, x[100000], cnt, ans;
signed main()
{
cin >> n;
for(int i = 1; i <= n; i *= 2)
for(int j = i; j <= n; j *= 5)
x[++cnt] = j;
sort(x + 1, x + cnt + 1);
int a = cnt;
for(int b = 1; b <= n; b++)
{
if(b % 2 == 0 || b % 5 == 0) continue;
while(a && x[a] > n / b) a--;
ans += a * (n / b);
}
cout << ans << endl;
return 0;
}
子任务 3:n \le 10^{12}
不妨把 d 写的更直接一点,换成 f(x),表示 [1, x] 中只含2, 5 两个因子的数的个数。前面那个式子等价于: