zoukankan      html  css  js  c++  java
  • 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
    ",ans);
    		return 0;
    	}
    	B.Update(b);
    	printf("%lld
    ",B.Query(m+n)-B.Query(n-1));
    	return 0;
    }
    
  • 相关阅读:
    ES6 Promise 用法转载
    移动端滚动性能优化
    Python之禅
    Day01~15
    Python
    第一章 Java起源
    IMP-00009: 导出文件异常结束 imp
    浏览器访问网页的详细内部过程
    数据库连接池
    连接数据库 六大步骤
  • 原文地址:https://www.cnblogs.com/qswx/p/9509246.html
Copyright © 2011-2022 走看看