Description
Philips得到了一个计算器,这个计算器有两个整数单元,一开始,第一个单元包含数字1,第二个单元包含数字0。 这个计算器支持一以下两种操作:
1.假设第一个单元的数字为a,第二个单元的数字为b,那么将第二个单元的数字改成b+1。
2.假设第一个单元的数字为a,第二个单元的数字为b,那么将第一个单元的数字改成a*b。
现在Philips想知道,有多少个正整数x(l<=x<=r)满足, 存在一种方式从计算器初始状态开始,操作不超过p步之后使得第一个单元中的数字为x。
Input
共一行,为三个空格隔开的正整数(2<=l<=r<=10^9, 1<=p<=100)
Output
输出仅一行一个整数ans,即为问题所求。
Sample Input
输入1:
2 10 3
输入2:
2 111 100
输入3:
2 111 11
Sample Output
输出1:
1
输出2:
106
输出3:
47
Data Constraint
对于30的数据p<=16。
对于100%的数据2<=l<=r<=10^9, 1<=p<=100。
分析:
因为最多操作
步,所
,所以
不会有大于
的约数,那一定不会有大于
的质因数。
我们把 范围内的所有小于等于 的质因数跑出来,然后dfs出 内可以被表示成小于等于 的质因数乘积的数跑出来,然后从小到大排序。
当 时,这样的数不超过 个,也就是只有这些数能被表示出来。
后面使用dp求步数,设
表示表示二元组的第一维变为
,第二维变为
时,操作二的次数(乘操作),显然有
那么
我们发现第二维不能直接用 ,因为 是 的,所以要把第二维设为是在答案数列中的第 位,这样的话,就不能直接用下标表示 了,但是我们发现,当我们 在不断增大的时候, 也是单调增的,所以我们可以开一个指针,当 右移时跟着就好了。
这样开 数组会爆空间,考虑滚动掉第二维,这样空间就是 级了。
时间复杂度是 的,但是 最大可以达到 ,还是跑不过。考虑玄学优化一下,我们发现可以改为枚举 ,此时如果 已经大于数列中最大的数,那么就可以break掉了,应该是能过的。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
const int maxn=3e6+7;
using namespace std;
int l,r,p,cnt,tot;
int not_prime[107],prime[107];
int a[maxn],f[maxn];
bool b[maxn];
void getprime(int n)
{
for (int i=2;i<=n;i++)
{
if (!not_prime[i])
{
prime[++tot]=i;
}
for (int j=1;j<=tot;j++)
{
if (i*prime[j]>n) break;
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
}
void dfs(int x,int k)
{
a[++cnt]=k;
for (int i=x;i<=tot;i++)
{
if (prime[i]*(long long)k<=r) dfs(i,k*prime[i]);
}
}
int main()
{
scanf("%d%d%d",&l,&r,&p);
getprime(p);
dfs(1,1);
sort(a+1,a+cnt+1);
for (int i=1;i<=cnt;i++) f[i]=1156161;
f[1]=0; b[1]=1;
int ans=0;
for (int i=2;i<=p;i++)
{
int j=i;
for (int k=1;k<=cnt;k++)
{
while ((j<=cnt) && (a[j]!=a[k]*i)) j++;
if (j>cnt) break;
if (f[k]+1<f[j]) f[j]=f[k]+1;
if ((a[j]<l) || (b[j])) continue;
if (f[j]+i<=p) b[j]=1,ans++;
}
}
printf("%d",ans);
}