zoukankan      html  css  js  c++  java
  • CF261E Maxim and Calculator (质数,完全背包)

    CF261E Maxim and Calculator

    题目大意:

    有两个初始参数 $ a=1 $ , $ b=0 $ ,你可以对它们进行两个操作: $ b~+=1 $ 或 $ a~ imes =b $ ,最终的 $ a $ 才是你所得到的数 。 现在给你三个数 $ l,r,p $ ,让你求在区间 $ [l,r] $ 内可以用不超过 $ p $ 次操作得到的数的个数。数据范围: $ (2<=l<=r<=10^{9},1<=p<=100) $



    $ solution: $

    很神仙的一道题,当时看了很久只能想到:每个我所得到的数,一定是若干个可能不同的 $ b $ 的乘积,。又因为 $ b $ 每次只能加1,所以我们如果要用最少的步数得到一个数 $ a $ ,最终 $ b $ 的大小一定是 $ b $ 的最大质因子!同理,最大质因子大于 $ p $ 的数一定不可通过少于 $ p $ 的次数得到

    当时的想法是线性筛最大质因子,然后暴力判断,复杂度 $ O(n) $ 显然超时。然后死活没想到可以筛 $ p $ 以下质数所构成的数( $ 10^9 $ 里面不超过 $ 3 imes 10^6 $ 个 )!

    好吧,回归正题。根据上面我们的分析,所有最大质因子大于 $ p $ 的数一定不可通过少于 $ p $ 的次数得到,所以我们可以找出所有最大质因子在 $ p $ 以内的数。怎么求?这其实等同于求 $ p $ 以内的所有质数可以构造的数(注意题目说 $ 2leq r $ ,所以不考虑1)(然后我们现线性筛质数,再搜索一下即可)。然后我们发现这样的数不超过 $ 3 imes 10^6 $ 个,我们用一个数组 $ a[] $ 存起来。于是我们考虑怎么判断 $ a[] $ 里面的数是否能在 $ p $ 次操作内得到。

    首先我们要明白一个点:所有 $ a[] $ 里的数都可以通过 $ a[] $ 里比它小的数构造出来。证明:对于 $ a[] $ 里的数 $ i $ ,如果它的最大质因子为 $ k $ ,那么 $ i/k $ 一定在 $ a[] $ 中,因为 $ i/k $ 的所有质因子都小于 $ p $ 。于是我们考虑 $ f[i][j] $ 表示 $ b $ 的大小为 $ i $ 时 $ j $ 的最小构造步数。我们从小到达枚举 $ b $ ,每个 $ b $ 可以选用很多次,这不就是一个完全背包吗?不过是 $ b $ 要被乘上去而已!注意我们的 $ a[] $ 数组是离散化的,我们先排一遍序,然后用单调队列找到 $ a[k]=a[j]*i $ 即可做到 $ O(1) $ 转移!

    $ f[i][k]=f[i][j]+1 quad (~a[j] imes i=a[k]~) $

    复杂度: $ O(p imes 3 imes 10^6) $ ,很勉强



    $ code: $

    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<ctime>
    #include<cmath>
    #include<vector>
    #include<queue>
    #include<map>
    #include<set>
    
    #define ll long long
    #define db double
    #define rg register int
    
    using namespace std;
    
    int l,r,p,n; // n表示a数组的大小
    int ans,tt; // tt素数个数
    int a[3000005]; //最大质因子小于p的数的集合
    int f[3000005]; //构造a[i]这个数的最少步数
    bool d[3000005]; //是否已经加入贡献
    int pr[505]; //素数表
    bool vis[505]; //筛素数
    
    inline int qr(){
    	register char ch; register bool sign=0; rg res=0;
    	while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
    	while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
    	if(sign)return -res; else return res;
    }
    
    inline void prime(int x){ //线性筛素数
        for(rg i=2;i<=x;++i){
            if(!vis[i])pr[++tt]=i;
            for(rg j=1;j<=tt;++j){
                if(i*pr[j]>x)break;
    			vis[i*pr[j]]=1;
                if(!(i%pr[j]))break;
            }
        }
    }
    
    //找到最大质因子小于p的数
    //等效于我们构造小于p的质数所能构造的数
    inline void dfs(int i,ll v){ //i是当前轮到的质数
    	if(i>tt)return ; 
    	dfs(i+1,v); //不用这个质数
    	while(1){
    		v*=pr[i]; //不断选用这个质数
    		if(v>r)return ; //退出
    		dfs(i+1,v);
    		a[++n]=v; //记录这个数
    	}
    }
    
    int main(){
    	l=qr(); r=qr(); p=qr();
    	prime(p); dfs(1,1); a[++n]=1; //预处理
    	sort(a+1,a+n+1); f[1]=0;
    	for(rg i=2;i<=n;++i) f[i]=101; //赋初值无穷大
    	for(rg i=2;i<=p;++i){
    		for(rg j=1,k=1;j<=n;++j){
    			while(k<=n&&a[j]*i>a[k])++k; //因为a数组离散化,所以单调队列查找
    			if(k>n)break; if(a[j]*i!=a[k])continue;
    			f[k]=min(f[k],f[j]+1); //更新步数
    			if(d[k]||f[k]+i>p||a[k]<l)continue; //注意第二个判断
    			d[k]=1; ++ans; //步数在p范围内,计入答案
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    HDU 5273 Dylans loves sequence 暴力递推
    HDU 5285 wyh2000 and pupil 判二分图+贪心
    HDU 5281 Senior's Gun 贪心
    HDU 5651 xiaoxin juju needs help 逆元
    HDU 5646 DZY Loves Partition
    HDU 5366 The mook jong
    HDU 5391Z ball in Tina Town 数论
    HDU 5418 Victor and World 允许多次经过的TSP
    HDU 5642 King's Order dp
    抽屉原理
  • 原文地址:https://www.cnblogs.com/812-xiao-wen/p/11296562.html
Copyright © 2011-2022 走看看