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;
}