题目链接
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;
}