「解题报告」[luoguP6583]回首过去 (数论分块)

「解题报告」[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;
}	
原文地址:https://www.cnblogs.com/BruceW/p/13149498.html