题目大意
给定n、k、t。
对于一个数x,可以一步变成x/k(必须整除)或x-i(1<=i<=t)
求把n变成1的最少步数
DP
设f[i]表示i变成1的最少步数
显然f[i*k]=min(f[[i*k],f[i]+1)
那么对于减怎么办?
维护单调递增的单调队列,每次从队头取决策,如果队头不合法则踢出。
注意k=1或t=0
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=1000000+10;
int f[maxn],dl[maxn];
int i,j,k,l,t,n,m,ca,head,tail;
int main(){
scanf("%d",&ca);
while (ca--){
scanf("%d%d%d",&n,&k,&t);
fo(i,1,n) f[i]=n;
f[1]=0;
f[k]=1;
dl[head=tail=1]=1;
fo(i,2,n){
while (head<=tail&&dl[head]<i-t) head++;
if (head<=tail) f[i]=min(f[i],f[dl[head]]+1);
if ((ll)i*k<=n) f[i*k]=min(f[i*k],f[i]+1);
while (head<=tail&&f[i]<=f[dl[tail]]) tail--;
dl[++tail]=i;
}
printf("%d\n",f[n]);
}
}