CF261E Maxim and Calculator

洛咕

二元组$ (a,b)\(,可以变成\) (a,b+1)\(或\) (ab,b)\(.你有初始二元组\) (1,0)\(,给你区间\) [l,r]\(,和一个整数\) p\(,在区间内选一个数\) x\(,使\) (1,0)\(在不超过\) p\(步变化后,第一维的值变成\) x\(,求\) x$的个数.\(2<=l<=r<=1e9,1<=p<=100\)

分析:思维难度大.首先要发现最大质因数大于p的数一定不能在p步之内得到(假设最大质因数为maxn,因为该数一定要是乘上maxn才能得到,而maxn是个质数,只能每次+1得到,要加maxn-1次,然后再乘以次,总共maxn次)

所以我们线性筛先筛出p以内的所有质数,然后一个dfs求出所有最大质因数小于p的数放入a数组中(p最大为100,此时大概\(3*10^6\)个),现在我们考虑判断a数组中的数能否在p步之内得到即可.

然后我们把a数组从小到大排序,枚举i为步数,j为当前判断到的数为a[j],设\(f[j]\)表示要得到\(a[j]\)要乘的最少次数(记住f数组只计算乘的次数,不计算加1的次数),则当\(a[j]*i=a[k]\)时,\(f[k]=min(f[k],f[j]+1)\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=105;
const int M=5000005;
int l,r,p,tot,n,ans;
int v[N],prime[N];
int a[M],visit[M],f[M];
inline void get_prime(){
	for(int i=2;i<=p;++i){//线性筛素数筛出p以内的所有质数
		if(!v[i]){
			v[i]=i;
			prime[++tot]=i;
		}
		for(int j=1;j<=tot;++j){
			if(prime[j]*i>p||prime[j]>v[i])break;
			v[prime[j]*i]=prime[j];
		}
	}
}
//dfs求出所有最大质因数小于p的数
//num表示当前考虑到了第num个质数,now表示当前质数的乘积
inline void dfs(int num,ll now){
	if(num>tot)return;//全都考虑完了
	dfs(num+1,now);//不选当前的这个数
	while(1){//可以乘当前这个质数无数次
		now*=prime[num];//累乘
		if(now>r)break;//超过范围
		a[++n]=now;//记录
		dfs(num+1,now);//考虑下一个数
	}
}
int main(){
	l=read();r=read();p=read();
	get_prime();dfs(1,1);
	a[++n]=1;sort(a+1,a+n+1);
	f[1]=0;for(int i=2;i<=n;++i)f[i]=1e9;
	for(int i=2;i<=p;++i){
		for(int j=1,k=1;j<=n;++j){
			while(k<=n&&a[j]*i>a[k])++k;
			if(k>n)break;if(a[j]*i<a[k])continue;
			f[k]=min(f[k],f[j]+1);
			if(visit[k]||f[k]+i>p||a[k]<l)continue;
//解释一下第二个,f数组记录的要得到a[k]需要乘的步数,i是要加1的步数,两者相加才是总步数.
			visit[k]=1;++ans;
		}
	}
	printf("%d\n",ans);
    return 0;
}

posted on 2019-08-05 21:27  PPXppx  阅读(116)  评论(0编辑  收藏  举报