P6583 回首过去

题意:

给定正整数 \(n\),求出有序整数对 \((x,y)\) 的个数,满足 \(1≤x,y≤n\)\(\frac{x}{y}\) 可以表示为十进制有限小数。

\(1\le n\le 1e12\)

解题思路:

论如何学会枚举

发现约分之后若想使 \(\frac{x}{y}\) 为十进制有限小数,\(y\) 本身只含 \(2,5\) 两种因子。

考虑这么一个形式:

\[\frac{bc}{ac} \]

其中 \(a\) 本身只含 \(2,5\) 两种因子,\(c\) 本身不含 \(2,5\) 两种因子,\(b\) 为任意数。

枚举 \(a\) ,在 \(\lfloor{\frac{n}{a}}\rfloor\) 里找到满足条件的 \(c\) ,由于 \(b\) 为任意数,此时的贡献其实就是 \(\lfloor{\frac{n}{c}}\rfloor\) ,这个解法复杂度是 \(O(n)\) 的,能过 \(\operatorname{80pts}\)\(\operatorname{subtask}\)

能不能再给力一点儿?

改变枚举顺序,枚举 \(c\) ,那么答案实际上变成了如下形式:

\[f(\lfloor{\frac{n}{c}}\rfloor)\times \lfloor{\frac{n}{c}}\rfloor \]

其中的 \(f(x)\) 表示 \([1,x]\) 的范围内本身只含 \(2,5\) 两种因子的数字个数。

然后惊喜地发现整除分块可以做。

然后惊喜地发现不会找到某区间之内不含\(2,5\) 两种因子的数

考虑容斥,设该区间为 \([1,r]\) ,符合条件的数字个数即为:

\[r-\lfloor\frac{r}{2}\rfloor-\lfloor\frac{r}{5}\rfloor+\lfloor\frac{r}{10}\rfloor \]

(所有数减去 \(2\) 的倍数再减去 \(5\) 的倍数,发现关于 \(10\) 的倍数减重了,加上就可以力 )

最后差分即可。

时间复杂度 \(O(\sqrt n)\)

代码:

#include <cstdio>
#include <algorithm>
#define Reg register
#define int long long
using namespace std;
const int maxn=200100;
int n,a[maxn],cnt,ans,v;
inline int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<1)+(s<<3)+(ch^48);
        ch=getchar();
    }
    return s*w;
}
//行,我是神必
signed main(){
    n=read();
    for(Reg int i=1;i<=n;i*=2)
        for(Reg int j=i;j<=n;j*=5)
            a[++cnt]=j;
    sort(a+1,a+1+cnt);
    for(Reg int i=1,j=0;i<=n;i=j+1){
        int p=n/i;
        j=(!p)?n:n/p;
        while(a[cnt]>p) --cnt;
        v=j-i+1;
        v-=j/2ll-(i-1)/2ll;
        v-=j/5ll-(i-1)/5ll;
        v+=j/10ll-(i-1)/10ll;
        ans+=v*p*cnt;
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2022-11-08 14:22  Broken_Eclipse  阅读(40)  评论(1编辑  收藏  举报

Loading