洛谷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;
}
posted @ 2020-06-02 17:07  GsjzTle  阅读(205)  评论(0编辑  收藏  举报