B光滑数

Description

B为一个正整数,如果一个自然数N的因子分解式中没有大于B的因子,我们就称N是一个B光滑数。请你编一个程序,求出某个区间中所有的B光滑数的个数。

Analysis

首先,打一个暴力,枚举n~n+m,找出每个数的最大质因子,不大于b就算上,输出答案。

不错,得了50分。

想到:b不一定是质数,所以要找到最接近b的质数。可以用欧拉线性筛。

接下来思考新的做法,递推递归是一个不错的选择。一开始我选择了尝试递推,发现:b光滑数一定包括b-1的光滑数,所以找到一个状态转移的方式:

B[i]=B[i-1]

上面说的并不准确,因为b-1不是质数,事实上b-1指的是b的前一个质数。

当然,状态转移不止这些,可以发现前n个数中的B光滑数除去B-1光滑数后只剩下B的倍数,而B的倍数除去所有B后一定是一个B-1光滑数,又可以得出:

B[a][b]=B[a][b-1]+B[a/b][b-1]+B[a/b/b][b-1]+...+B[1][b-1]

这个方程太长,能不能简化呢,可以。利用愤怒的小鸟(无限背包问题)思想可以发现,B[a/b][b]可能已经包含了b的n倍,所以只要由其转移而来即可。

B[a][b]=B[a][b-1]+B[a/b][b]

然而,直接递推完全存不下数组..所以放弃递推打起了递归。

思考一下边界问题:B[1][b]一定是1,B[a][1]也是1,对于B[a][b]当a<=b时,显然a个数都是b光滑数,所以边界已经确定好了。

85分!1WA2TLE,那么程序还存在问题,发现大量的状态重复运算,所以针对小规模数据进行记忆化(10000*3000)基本上足够。

95分!错了一个小点,应该是数据较大时想当然的处理在数据小时出现了错误,所以对于1000以内数据进行了暴力特判,过了。

Code 高于O(b·logbm)

#include <bits/stdc++.h>
#define ll long long
const ll N=1e6;
class B_Smooth_Number{
    private:
        int b;
        ll B[10001][3001];
        bool not_prime[N];
        std::vector <ll> prime;
        void Euler(){
            for(ll i=2;i<N;i++){
                if(!not_prime[i])
                    prime.push_back(i);
                for(ll j=0;prime[j]*i<N&&j<prime.size();j++){
                    not_prime[prime[j]*i]=1;
                    if(!(i%prime[j]))break;
                }
            }
        }
        ll Search(ll s,ll f){
            if(f==-1)return 1;
            if(s<=prime[f])return s;
            if(s<=10000&&f<=3000&&B[s][f])return B[s][f];
            ll ans=Search(s,f-1)+Search(s/prime[f],f);
            if(s<=10000&&f<=3000)B[s][f]=ans;
            return ans;
        }
    public:
        void Update(ll x){
            Euler();
            for(ll i=0;i<prime.size();i++)
                if(prime[i]>x){
                    b=i-1;
                    return;
                }
        }
        ll Query(ll x){
            return Search(x,b);
        }
}B;
int revolve(int x){
    int s=0;
    for(int i=2;i<=x;i++)
        while(!(x%i)){
            s=std::max(s,i);
            x/=i;
        }
    return s;
}
int main(){
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
    ll n,m,b;
    scanf("%lld%lld%lld",&n,&m,&b);
    if(n+m<1000){
        int ans=0;
        for(int i=int(n);i<=int(n+m);i++)
            if(revolve(i)<=b)ans++;
        printf("%d\n",ans);
        return 0;
    }
    B.Update(b);
    printf("%lld\n",B.Query(m+n)-B.Query(n-1));
    return 0;
}

转载于:https://www.cnblogs.com/qswx/p/9509246.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值