题目描述(传送门)
题目翻译:
Monocarp正在玩电脑游戏,他现在是一个巫师学徒,只会一个技能。幸运地是,这个技能可以打败怪兽。当前有n个怪兽,第i个怪兽在第ki 秒出现,且该怪兽的生命值为hi。其中hi ≤ ki,且ki互不相同。
Monocarp可以在整秒时刻(从1开始,1,2,3,…)发出技能,技能的伤害为:如果前一秒他没有发出技能,则此时技能的伤害值为1;如果前一秒技能的伤害值为x,则他可以选择此时技能的伤害值为x+1或1。使用一个伤害值为x的技能,将消耗x个mana,并且mana不可再生。
为了杀死第i个怪兽,Monocarp必须在ki时刻,发出技能,并且技能的伤害值至少为hi。
Monocarp可以在没有怪兽出现的时刻发出技能。
计算杀死所有的怪兽,至少需要消耗多少个mana。
题目保证可以杀死所有的怪兽。
解题思路:
为了杀死在第ki秒出现的生命值为hi的怪兽,Monocarp需要提前做好准备,使其技能的伤害值在第ki秒时至少为hi,用直方图来表示技能在不同时刻的伤害值,横向表示时间,纵向表示伤害值大小。
比如,假设全程只有一个生命值为5的怪兽,其在第6秒出现,则Monocarp最晚需要从第2秒开始,积累技能的伤害值,这样才能使技能的伤害值在第6秒达到5,并且消耗的mana也最少。
设怪兽的生命值为hi,则消耗的mana=(hi+1)*hi/2,可以发现消耗的mana可由该怪兽的生命值直接计算得出。
但题目没有这么简单,如果在第2秒到第6秒之间出现了其他怪兽,则可能产生影响。
比如在第4秒出现了一个生命值为4的怪兽
则为了杀死这两个怪兽,我们需要将生命值整体增加,变成这样
这时候mana的消耗量=(6+1)*6/2,发现这里的值6并不是某个怪兽的生命值了,而是由怪兽的生命值经过增加得到的。设最高蓝柱的高度为height
,由height可以直接计算出消耗的mana量,height最开始是某个怪兽的生命值,并且随着其他怪兽的出现,height可能会相应地增大。就上面的例子而言,height的增值为4-3=1(注意看技能伤害值在第4秒的变化)。
而如果出现的怪兽的生命值小于等于当前时刻技能的伤害值,则height不发生改变。比如在第2秒出现一个生命值为1的怪兽:
可以发现,当前的生命值就足以消灭该怪兽。
至此,我们可以得出代码实现的思路,根据怪兽出现的时间,从后往前遍历,将height的初始值设为最后一个怪兽的生命值,再判断在当前蓝色阶梯区域内是否有其它怪兽,并比较其生命力与对应时刻技能的伤害值,若其生命力大于技能的伤害值,则增大height,此时蓝色阶梯区域也会相应地扩大。直到将蓝色阶梯区域内的怪兽都遍历结束,则可以计算出这一部分的mana消耗量,再继续遍历下一个怪兽。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 10010
#define INF 0x7fffffff
using namespace std;
int main(){
// freopen("1.txt","r",stdin);
ios::sync_with_stdio(false);
cin.tie(0);
long long k[N],h[N],sum;
int t,n,cur;
cin>>t;
while(t--){
cin>>n;
sum=0;
for(int i=1;i<=n;i++) cin>>k[i];
for(int i=1;i<=n;i++) cin>>h[i];
//这里用某个怪兽的生命值h[cur]保存height,因为height会发生变化,所以h[cur]也会变化,但不会影响结果
cur=n;
for(int i=n-1;i>=1;i--){
if(k[cur]-h[cur]<=k[i]-h[i]){//表示当前技能的伤害值足以消灭该怪兽
continue;
}else if(k[cur]-h[cur]<k[i]){//增加height
h[cur]+=(h[i]-h[cur]-k[i]+k[cur]);
}else{//表示蓝色阶梯区域内的怪兽都遍历完了,计算这部分的mana值
sum+=(1+h[cur])*h[cur]/2;
cur=i;
}
}
sum+=(1+h[cur])*h[cur]/2;
cout<<sum<<endl;
}
return 0;
}