「解题报告」[luoguP6583]回首过去 (数论分块)
题面
题意
给定正整数 (n), 求出有序整数对 ((x,y)) 的个数, 满足 (1le x,y le n) 且 (frac{x}{y}) 可以表示为十进制有限小数.
数据范围
(1 le n le 10^{12})
思路
若 (frac{x}{y}) 是十进制有限小数, 则 (frac{y}{gcd(x,y)}) 的质因子只有 (2) 和 (5).
枚举 (i,j,d) , 表示 (y=2^i5^jcdot d) , 则 (x) 的数量为 (leftlfloor frac{n}{d} ight floor) , 可以数论分块.
枚举 (d) 的时候, 需要满足 (d) 不能整除 (2) 或 (5) , 所以数论分块的时候要容斥一下.
因为每 (10) 个数中必定有 (6) 个数是 (2) 或 (5) 的倍数, 所以数论分块的时候先处理整十的部分, 对于剩下的暴力枚举一下即可.
代码
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll n,ans,q[400],cnt,tms;
bool cmp(ll x,ll y){ return x>y; }
int main(){
cin>>n;
ll t2=1;
while(t2<=n){
ll t5=1;
while(t2*t5<=n){
q[++cnt]=t2*t5;
t5*=5ll;
}
t2*=2ll;
}
ll sum=0; q[0]=n+1;
sort(q+1,q+1+cnt,cmp);
for(int p=1;p<=cnt;p++){
for(ll i=n/q[p-1]+1,j;i<=n/q[p];i=j+1){
j=min(n/q[p],n/(n/i));
ll k=(j-i+1)/10,num=4*k;
for(k=i+k*10;k<=j;k++)
if(k%2&&k%5) num++;
sum+=num*(n/i);
}
ans+=sum;
}
printf("%lld
",ans);
return 0;
}