[BC#89B]Fxx and game

题目大意

给定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]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值