P6583 回首过去 题解

2333333333

考虑首先合法的答案一定是分数约分后分母的质因数只有 2,52,5。也就是说

\frac{x}{y}=\frac{\frac{x}{\gcd(x,y)}}{2^p5^q}yx​=2p5qgcd(x,y)x​​

其中 p,qp,q 都是极大的。稍微化一下可以发现

y=\gcd(x,y)\times 2^p5^qy=gcd(x,y)×2p5q

那么就可以对每一个 yy 求有多少个 xx 是合法的。考虑 xx 一定可以写作 d\times \gcd(x,y)d×gcd(x,y),那么也就是说由于 \gcd(x,y)gcd(x,y) 固定(因为 2^p5^q2p5q 极大)要统计有多少个本质不同的 dd ,因为 xx 只能在 [1,n]\cap \mathbb {Z_+}[1,n]∩Z+​ 中取值,所以不难发现一共有 \left\lfloor\dfrac{n}{\gcd(x,y)}\right\rfloor⌊gcd(x,y)n​⌋ 种取法。所以 8080 分可以这么写

        for (int i = 1 ; i <= n ; ++ i){
            int j = i, o ;
            while (j % 2 == 0) j /= 2 ;
            while (j % 5 == 0) j /= 5 ;
            ans += n / j ;
        }

考虑这个式子是一个下取整的形式,即

\sum _{i=1}^n\left\lfloor\dfrac{n}{\zeta(i)}\right\rfloor \qquad(1)i=1∑n​⌊ζ(i)n​⌋(1)

其中 \zeta(i)ζ(i) 是最大的不含质因子 2,52,5 的 ii 的因子。考虑调换枚举顺序,枚举 \zeta(i)ζ(i),会有

(1)=\sum_{k=1}^{n}\left\lfloor\dfrac{n}{k}\right\rfloor\times [2\nmid k]\times[5\nmid k]\times {\rm count(}\left\lfloor\dfrac{n}{k}\right\rfloor)(1)=k=1∑n​⌊kn​⌋×[2∤k]×[5∤k]×count(⌊kn​⌋)

其中最后一个 \rm count(t)count(t) 表示 1\sim t1∼t 之间有多少只包含 2,52,5 作为质因数的数。原因很简单,因为我们枚举的是 \zeta(i)ζ(i),\zeta(i)ζ(i) 需要搭配上 2^p5^q2p5q 才会有效,所以计数有多少 \leq n≤n 的数包含 kk 就是相当于计数有多少 \leq \left\lfloor\dfrac{n}{k}\right\rfloor≤⌊kn​⌋ 的数包含 2^p5^q2p5q。于是这么做就是 O(\sqrt n\log ^2 n)O(n​log2n) 的了。稍微预处理一下 \rm countcount 就可以做到 \sqrt nn​ 了。


ll n ;
ll res ;
ll ans ;

#define gcd __gcd

ll sum(ll x){
    return (x - (x / 5) - (x >> 1) + (x / 10)) ;
}
ll all_2_5(ll x){
    ll ret = 0 ;
    ll re2 = log(x) / log(2) + 1 ;
    ll re5 = log(x) / log(5) + 1 ;
    ll sum2 = 1 ; ll sum5 ;
    for (int i = 0 ; i <= re2 ; ++ i){
        sum5 = 1 ;
        for (int j = 0 ; j <= re5 ; ++ j){
            if (sum5 * sum2 > x) break ;
            sum5 = 5ll * sum5 ; ++ ret ;
        }
        sum2 = sum2 * 2ll ;
    }
    return ret ;
}

int main(){
    cin >> n ;
    if (n <= 10000000){
        for (int i = 1 ; i <= n ; ++ i){
            int j = i, o ;
            while (j % 2 == 0) j /= 2 ;
            while (j % 5 == 0) j /= 5 ;
//        while (j % 10 == 0) j /= 10, re10 *= 10 ;
		//cout << re2 << " " << re5 << " " << re10 << '\n' ;
//		cout << j << '\n' ;
            ans += n / j ;
//		cout << ans << '\n' ;
        }
    }
    else {
//        cout << all_2_5(10) << '\n' ;
        for (ll l = 1, r ; l <= n ; l = r + 1){
            r = n / (n / l) ;
            ans += (sum(r) - sum(l - 1)) * 1ll * (n / l) * all_2_5(n / l) ;
        }
    }
    cout << ans << '\n' ;
    return 0 ;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值