【CF261E】Maxim and Calculator
题目
题目链接:https://www.luogu.com.cn/problem/CF261E
二元组\((a,b)\),可以变成\((a,b+1)\)或\((ab,b)\) 。
你有初始二元组\((1,0)\),给你区间\([l,r]\),和一个整数\(p\),在区间内选一个数\(x\),使\((1,0)\)在不超过\(p\)步变化后,第一维的值变成\(x\),求\(x\)的个数。
思路
首先\(p\leq 100\),这意味着\(b\)最大是\(100\)。
那么如果一个数\(x\)存在一个质因子\(d\),且\(d>p\),那么显然在\(p\)步之内是没法使\(a\)变成\(d\)的。
所以先\(dfs\)出\([1,r]\)之内所有质因子都在\(p\)以内的数字。在\([1,10^9]\)中这样的数字不会超过\(3\times 10^6\)个。
然后设\(f[i][j]\)表示\(a,b\)分别等于\(i,j\)时的最小转移次数。做背包即可。
\[f[i][j]=min(f[\frac{i}{j}][j],f[i][j-1])+1
\]
注意实际时\(i\)应该为上文求出来的数列的第\(i\)项,且第二维应滚动。
代码
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=3000010,M=110;
const int prime[26]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};
int l,r,n,m,pos,ans,a[N],f[2][N];
bool vis[N];
void dfs(int x,int dep,int last)
{
//if (dep>m) return;
a[++n]=x;
for (int i=last;i<=pos;i++)
if (1LL*x*prime[i]<=(long long)r)
dfs(x*prime[i],dep+1,i);
}
int main()
{
scanf("%d%d%d",&l,&r,&m);
pos=25;
for (int i=1;i<=25;i++)
if (prime[i]>m)
{
pos=i;
break;
}
dfs(1,0,1);
sort(a+1,a+1+n);
memset(f,0x3f3f3f3f,sizeof(f));
f[0][1]=0;
for (register int j=1;j<=m;j++)
{
int k=1;
for (register int i=1;i<=n;i++)
{
while (a[i]>a[k]*j && k<=n) k++;
if (a[k]*j==a[i]) f[j&1][i]=min(f[j&1][k],f[(j+1)&1][i])+1;
else f[j&1][i]=f[(j+1)&1][i]+1;
if (!vis[i] && l<=a[i] && a[i]<=r && f[j&1][i]<=m)
{
vis[i]=1;
ans++;
}
}
}
printf("%d\n",ans);
return 0;
}