【HDU 5945】Fxx and game(DP+单调队列)
啊啊啊啊啊啊啊。。。BC第二题就这么难了……好菜啊……这游戏没法玩了啊……。。。
膜一下队伍主力 金QAQ巨。。。现两号BC金。。Orz……@a1s4z5
顺吐槽一发……大家BC页面会卡么……宿舍进BC卡得要死……具体卡成什么样子……校园网进不去热点也卡的要命,耐心等待题面一帧帧 浮现 出来……然后已经被甩出去好几条街了……然后……咦……这A题坑点很迷啊,Hack肯定很爽啊,耐心等待页面 一条条 出现后……双击room里一人的A题……我选择放弃。。。。
从此BC成路人……
言归正传,这题还是很好的……即使因此分掉了一大截……摔。。
具体的:给出x,t,k,要将数字x变成1。操作有二:
1.把数字x变成数字y(0 <= x-y <= t),消耗一步。
2.x->x/k(if x%k == 0),消耗一步。
一开始的想法很犀利……定义 dp[i] 为从x变成i的最少步数,那么答案其实是 min(dp[i])(1<=i<k) ,因为到k的话肯定会/k变成1,其实就是dp[1],所以只考虑1~k-1。
然后对于i(1 <= i < k),找到最大的y,满足y%k == 0 && y/k == i && y <= x
然后计算从x变成y,然后从y变成i,从i变成1。前后除t即可,中间就有点暴力了。。然后就爆炸了……然后刚才发现有地方写搓了。。然后改了后测了一些数据发现这种贪心是有问题的……然后还是爆炸了……
还是规规矩矩的做法吧,dp[i]表示i变成1的最少步数。
那么如果i%k != 0 那么dp[i] = min(dp[j])+1(0 <= i-j <= t)
如果i%k == 0,最小值里再取个dp[i/k]。
暴力的话会超时,维护一个跟i距离t内的单调队列,队列里的数值从左到右单调递增,右边入队左边取值即为t内最少步数。
代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
#define Pr pair
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-8;
const int maxn = 1123456;
int s[maxn];
int dp[maxn];
int main()
{
//fread("");
//fwrite("");
int t,T,l,r,x,k;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&x,&k,&t);
dp[1] = 0;
l = 0;
r = -1;
s[++r] = 1;
for(int i = 2; i <= x; ++i)
{
if(i%k == 0 && k != 1) dp[i] = dp[i/k]+1;
if(l <= r)
{
if(i%k == 0 && k != 1) dp[i] = min(dp[i],dp[s[l]]+1);
else dp[i] =dp[s[l]]+1;
}
while(l <= r && dp[i] <= dp[s[r]])
r--;
s[++r] = i;
while(l <= r && i-s[l] >= t) ++l;
}
printf("%d\n",dp[x]);
}
return 0;
}