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;
    }
    
    
  • 相关阅读:
    vm12序列号
    三星手机官方固件下载
    MSTP故障处理手册
    分享一个高清壁纸网站
    ThinkPad X220 完美黑苹果 Hackintosh OS X 10.11 El Capitan
    一句命令激活windows/office
    Win10+VMplayer12中U盘无法挂载解决
    记一次金士顿DT100 G3 32G修复
    飘雪代码2枚
    禁用安全模式
  • 原文地址:https://www.cnblogs.com/812-xiao-wen/p/11296562.html
Copyright © 2011-2022 走看看