P6583 回首过去 题解
不错的 Idea。当然如果熟练莫反就是另一回事,但我不熟练(
注意到如果最简分数 \(\dfrac{x}{y}\) 是有限小数,那么 \(y\) 只能含有 2,5 这两个质因子(当然可以为 1)。
然而分数并不一定最简,所以考虑将分数变成 \(\dfrac{xg}{yg},xg,yg\le n\) 这个形式,其中保证 \(\dfrac{x}{y}\) 最简,\(y\) 只含有 2,5 这两个质因子(或者为 1),\(x\) 任意,\(g\) 不能含有 2,5 这两个质因子。
显然合法的小于等于 \(n\) 的 \(y\) 可以预处理,因此考虑枚举 \(y,g\),注意到枚举 \(y\) 时 \(y\) 限制过大不好化简,只能枚举出符合条件的 \((y,g)\) 二元组的个数(人话:只能做到 \(O(n)\),可以自己手玩一下),因此考虑枚举 \(g\),此时 \(y\in[1,\lfloor\dfrac{n}{g}\rfloor]\),然后由于 \(y\) 只含有 2,5 这两个质因子,所以个数是很少的,又因为 \(g\) 递增时 \(y\) 递减,所以是单调的,能做到 \(O(n)\)(实际上枚举 \(y\) 也是利用这一点)。
注意到 \(n\le10^{12}\) 因此猜想应该是个根号算法,考虑令 \(f(\lfloor\dfrac{n}{g}\rfloor)\) 表示当前枚举 \(g\) 时合法的 \(y\) 的个数,那么最终答案如下:
发现这是个整除分块的形式,但讨厌的是限制条件 \(2\not\mid g,5\not\mid g\),考虑去掉这个限制条件,整除分块过程中处理出一段 \(\lfloor\dfrac{n}{g}\rfloor\) 相同的区间 \([l,r]\) 后,考虑计算这个区间内满足限制条件的 \(g\) 的个数 \(c\),这个可以简单容斥,\(c=g(l,r,1)-g(l,r,2)-g(l,r,5)+g(l,r,10)\),\(g(l,r,x)\) 表示 \([l,r]\) 内 \(x\) 的倍数的个数,处理出 \(c\) 后整个区间的贡献就是 \(c\times f(\lfloor\dfrac{n}{g}\rfloor)\lfloor\dfrac{n}{g}\rfloor\)。
Code:
/*
========= Plozia =========
Author:Plozia
Problem:P6583 回首过去
Date:2022/10/20
========= Plozia =========
*/
#include <bits/stdc++.h>
typedef long long LL;
const int MAXN = 1e6 + 5;
LL n, a[MAXN], cnta;
LL Read()
{
LL sum = 0, fh = 1; char ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
return sum * fh;
}
LL Min(LL fir, LL sec) { return (fir < sec) ? fir : sec; }
LL g(LL l, LL r, LL x) { return r / x - (l - 1) / x; }
int main()
{
n = Read(); for (LL i = 1; i <= n; i <<= 1) for (LL j = i; j <= n; j *= 5) a[++cnta] = j;
LL ans = 0; int p = cnta; std::sort(a + 1, a + cnta + 1);
for (LL l = 1, r; l <= n; l = r + 1)
{
if (n / l == 0) r = n; else r = Min(n / (n / l), n);
while (p > 0 && a[p] > n / l) --p;
ans += (g(l, r, 1) - g(l, r, 2) - g(l, r, 5) + g(l, r, 10)) * (n / l) * p;
}
printf("%lld\n", ans); return 0;
}