洛谷P6583 回首过去
题目链接
https://www.luogu.com.cn/problem/list?keyword=6583&page=1
题目大意
给定一个n,求有多少对 (x , y) 满足 1 <= x <= n , 1 <= y <= n 且 x / y 是有限小数
解题思路
首先有限小数的定义为当分数为最简形式时分母只包含 2 、5 两个质因子
于是我们可以就用 $\dfrac {bc}{ac}$ 来表示任何一个有限小数
( 其中 a 为 2、5 两个质因子构成的数 , c 为不包含 2、5 两个质因子的数 , b 随意 )
我们可以考虑枚举 c , 那么 $ans=\sum ^{n}_{c=1}\dfrac {n}{c}\times f\left( \dfrac {n}{c}\right) $
其中 (n / c) 为 b 的取值范围 , f(n / c) 为 [1 , n / c] 内 a 的个数
对于 f(n / c) 它会随着 c 的增大单调递减 , 那么我们总共只需要 O(n) 的复杂度就可求出
而 $\sum ^{n}_{c=1}\dfrac {n}{c}$ 又很显然是个经典的整除分块式 , 所以再套个整除分块的模板就差不多
不过值得注意的是这里的 c 是不包含 2、5 两个质因子的数 , 所以对于每个块我们要减去包含 2、5 两个质因子的数
实现起来很简单 , 根据容斥原理减去块内 2 的倍数的数 , 再减去块内 5 的倍数的数 , 然后再加上块内 10 的倍数的数即可
AC_Code
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 3e5 + 10; int a[N] , m , now = 1; int f(int x) { while(a[now] > x && now ) now -- ; return now ; } signed main() { ios::sync_with_stdio(false); int n ; cin >> n; for(int i = 1 ; i <= n ; i *= 2) for(int j = 1 ; j * i <= n ; j *= 5) a[++ m] = i * j; now = m; sort(a + 1 , a + 1 + m); int ans = 0; for(int l = 1 , r ; l <= n ; l = r + 1) { r = n / (n / l); int cnt1 = r - l + 1 , cnt10 = r / 10 - (l - 1) / 10; int cnt2 = r / 2 - (l - 1) / 2 , cnt5 = r / 5 - (l - 1) / 5; int sum = cnt1 - cnt2 - cnt5 + cnt10; ans += sum * (n / l) * f(n / l); } cout << ans << '\n'; return 0; }
凡所不能将我击倒的,都将使我更加强大