二元组$ (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;
}