P6538回首过去
题意:
求出有序整数对 的个数,满足 且 可以表示为十进制有限小数。
- Subtask 1(40 分),;
- Subtask 2(40 分),;
- Subtask 3(20 分),。
思路:
10进制小数必然可以写成 的形式,即分母为 ,即
做法一
枚举x,y为 ,约分gcd为 ,判断分母
复杂度:
做法二
把分数分解可以写成以下形式:
其中a只含2和5的因子,c为不包含2和5的因子,b的范围为
a可以预处理出来,枚举a值,再枚举c值,每次加上 (即b的个数),分母[1,n]都被枚举了一遍
复杂度:
做法三
发现答案会加上很多个 ,考虑整除分块 性质: 最大值范围限定为n,即随着c的增大,a的最大值会减小
此时先枚举c的值,这样b还是 的任意数,a限定在 范围内,因此可以每次减少a的最大值使其小于 ,可以用一个数来维护a能满足的个数,c的范围内不能被2和5整除的个数可以利用类似前缀和的方法算出
定义 表示l,r内被x整除的个数
则c的个数为
每次将答案进行累加即可
复杂度:
cpp
signed main(){
cin.tie(0)->sync_with_stdio(0);
ll n;
vector<ll> a;
cin>>n;
for(ll i=1;i<=n;i*=2){
for(ll j=i;j<=n;j*=5)a.push_back(j);
}
sort(a.begin(),a.end());
int cnt=a.size()-1;
ll ans=0;
for(ll l=1,r;l<=n;l=r+1){
ll x=n/l;
//r=(x==0?n:n/x);
r=n/x;
while(cnt&&a[cnt]>x)cnt--;
ll v=r-l+1;
v-=r/2-(l-1)/2;
v-=r/5-(l-1)/5;
v+=r/10-(l-1)/10;
ans+=v*(cnt+1)*x;
}
cout<<ans<<"\n";
return 0;
}