CF261E Maxim and Calculator dp

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。

分析:
因为最多操作 p 步,所 b p ,所以 a 不会有大于 p 的约数,那一定不会有大于 p 的质因数。

我们把 [ 1 , p ] 范围内的所有小于等于 p 的质因数跑出来,然后dfs出 [ 1 , r ] 内可以被表示成小于等于 p 的质因数乘积的数跑出来,然后从小到大排序。

p = 100 r = 10 9 时,这样的数不超过 3 10 6 个,也就是只有这些数能被表示出来。

后面使用dp求步数,设 f [ i ] [ j ] 表示表示二元组的第一维变为 j ,第二维变为 i 时,操作二的次数(乘操作),显然有

f [ i ] [ j ] = m i n ( f [ i ] [ j / i ] + 1 , f [ i 1 ] [ j ] )

那么
s t e p [ j ] = min i = 1 p ( f [ i ] [ j ] + i )

我们发现第二维不能直接用 j ,因为 j 10 9 的,所以要把第二维设为是在答案数列中的第 j 位,这样的话,就不能直接用下标表示 j / i 了,但是我们发现,当我们 j 在不断增大的时候, j / i 也是单调增的,所以我们可以开一个指针,当 j 右移时跟着就好了。

这样开 f 数组会爆空间,考虑滚动掉第二维,这样空间就是 10 6 级了。

时间复杂度是 O ( p n ) 的,但是 n 最大可以达到 3 10 6 ,还是跑不过。考虑玄学优化一下,我们发现可以改为枚举 k = j / i ,此时如果 k i 已经大于数列中最大的数,那么就可以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);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81055754