jzoj4010-Philips and Calculator【搜索,dp】

正题

题目链接:https://jzoj.net/senior/#contest/show/3008/2


题目大意

两个数 ( a , b ) (a,b) ,两个操作

  1. ( a , b ) > ( a , b + 1 ) (a,b)->(a,b+1)
  2. ( a , b ) > ( a b , b ) (a,b)->(a*b,b)

p p 步以内 a a 能到达 [ l , r ] [l,r] 之间的多少个数


解题思路

到达 x x 的最小步骤就是将 x x 分解乘若干个数的乘积,使得最大数+数的个数最小。

首先答案肯定是能被 1   p 1\sim ~ p 之间的质数的乘积表示出来的,发现这些数并不多,可以用 d f s dfs 搜索出来并排序。

然后 d p dp f i f_{i} 表示到达 a i a_i 的最少操作 2 2 ,然后枚举操作 1 1 的数量进行转移,也就是枚举最大数 j j ,找到一个 a k j = a i a_k*j=a_i

然后有转移 f i = m i n { f i , f k + 1 } f_{i}=min\{f_i,f_k+1\}

时间复杂度 O ( 3 1 0 6 n ) O(3*10^6*n)


c o d e code

#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=3e6+10;
int pri[25]={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,P,maxx,ans,a[N],f[N],cnt,z;
bool v[N];
void dfs(int x,int k){
	a[++cnt]=x;
	for(int i=k;i<=z;i++){
		if((long long)x*pri[i]>r) return;
		dfs(x*pri[i],i);
	}
}
int main()
{
	scanf("%d%d%d",&l,&r,&P);
	while(z<24&&pri[z+1]<=P)z++;
	dfs(1,0);
	sort(a+1,a+1+cnt);
	memset(f,0x3f,sizeof(f));
	f[1]=0;
	for(int i=2;i<P;i++){
		int z=0;
		for(int j=1;j<=cnt;j++){
			while(a[z]*i<a[j])z++;
			if(a[z]*i==a[j])f[j]=min(f[j],f[z]+1);
			if(i+f[j]<=P)v[j]=1;
		}
	}
	for(int i=cnt;i>=1;i--){
		if(a[i]<l) break;
		if(v[i])ans++;
	}
	printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/104196826
今日推荐