CodeForces-Explosions? 题解
题目描述
你在玩一个利用魔法杀怪物的游戏。具体地,有一排
你有两种攻击方式:一种是普通攻击,你将消耗
“爆炸”攻击的机制如下:你可以选择消耗的能量数
- 若怪物
当前的生命值 ,那么该怪物生命值减去 ; - 若
,则该怪物被击杀,同时向两旁的怪物(即第 和 只怪物,如果有且还存活的话)造成 的伤害; - 若
的伤害足以杀死第 (或 )只怪物,即 (或 ),那么这只怪物同样被击杀并向两旁的怪物造成 (或 )的伤害。如此循环只到杀不死一直怪物为止。
你的目标就是先用普通攻击减少某些怪物的生命值或直接杀死某些怪物,然后用这样“链式”的“爆炸”攻击杀死所有怪物。注意怪物不会移动,例如第
你需要消耗最少的能量值来达成目标(包括普通攻击和“爆炸”攻击的),求出这个最小值。
题目分析
首先可以想到一个 的做法
注意到我们爆炸需要最后使用,也就是说我们要选择一个点作为爆炸的点,将所有的剩余部分全部炸完,也就是说我们爆炸的时候情况可以抽象成下图:
也就是说,当最后形成一个单峰函数的时候,就可以使用炸弹了。我们可以将这个图形分为两个部分,单调上升部分,炸弹投放点以及单调下降部分。
所以有一个很明显有一个朴素的做法,我们可以枚举每个点作为炸弹投放点,然后贪心地付出代价使其变成一个单调上升->下降图形,我们最后剩余的图形的值的总和越大。具体地讲,我们直接从每个枚举的点出发,向左右拓展,每次拓展所能节省的代价即为
for(int i=1;i<=n;i++){
int Temp=Line[i];
for(int j=i-1;j>=1;j--){
Temp=min(max(Temp-1,0),Line[j]);
Need[0][i]+=Line[j]-Temp;
}
Temp=Line[i];
for(int j=i+1;j<=n;j++){
Temp=min(max(Temp-1,0),Line[j]);
Need[1][i]+=Line[j]-Temp;[]
}
Ans=min(Ans,Need[1][i]+Need[0][i]+Line[i]);
}
这个实际上就是
然后就根据 的做法形成 的做法了
注意到我们要求出的剩余部分是一个单调的序列,既然是单调的,那我们自然能想到单调栈或者单调队列优化。
我们考虑一个转移,我们用
注意到这时
那么如何实时更新这个序列的总值呢,可以发现,每个点代表的权值,都是一个梯形的面积,所以我们可以知道一个点所能产生的权值,即是
如上图,
那么知道计算规则之后,我们只需要每次出栈的时候将它产生的贡献删除,入栈时加入贡献即可。
那么每次我们的
对于
那么统计答案的时候直接
代码
/*
* Author:Ehundategh
* Update:2023/10/18
* Title:explosion2.cpp
* You steal,I kill
*/
#include <bits/stdc++.h>
using namespace std;
#define MAXN 300010
int T,n;
long long Need[2][MAXN],Line[MAXN],Ans=0x7fffffffffffffff,PreSum=0,SuffSum=0;
class Container{
public:
int Head=1,Tail=0,Pos[MAXN];;
long long Sum=0;
void Push_Back(int a){Pos[++Tail]=a;}
void Pop_Back(){Tail--;}
int Back(){return Pos[Tail];}
bool Empty(){return Head>=Tail;}
}Q;
long long Calc(long long Height,long long Length){//计算元素贡献
Length=min(Length,Height);
return 1ll*(Height+Height-Length+1)*Length/2;
}
void Ehundategh_Clear(){//清空
PreSum=SuffSum=0;
Q.Head=1;Q.Tail=0;
Q.Sum=0;
Q.Push_Back(0);
Ans=0x7fffffffffffffff;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>T;
while(T-->0){
cin>>n;
Ehundategh_Clear();
for(int i=1;i<=n;i++) cin>>Line[i];
for(int i=1;i<=n;i++){
PreSum+=Line[i];
while(i-Q.Back()>Line[i]-Line[Q.Back()]&&!Q.Empty()){
Q.Sum-=Calc(Line[Q.Back()],(long long)(Q.Back()-Q.Pos[Q.Tail-1]));
Q.Pop_Back();
}
Q.Push_Back(i);
Q.Sum+=Calc(Line[Q.Back()],(long long)(Q.Back()-Q.Pos[Q.Tail-1]));
Need[0][i]=PreSum-Q.Sum;
}
Q.Head=1;Q.Tail=0;
Q.Sum=0;
Line[n+1]=0;
Q.Push_Back(n+1);
for(int i=n;i>=1;i--){
SuffSum+=Line[i];
while(Q.Back()-i>Line[i]-Line[Q.Back()]&&!Q.Empty()){
Q.Sum-=Calc(Line[Q.Back()],(long long)(Q.Pos[Q.Tail-1]-Q.Back()));
Q.Pop_Back();
}
Q.Push_Back(i);
Q.Sum+=Calc(Line[Q.Back()],(long long)(Q.Pos[Q.Tail-1]-Q.Back()));
Need[1][i]=SuffSum-Q.Sum;
}
for(int i=1;i<=n;i++){
if(Need[0][i]+Need[1][i]+Line[i]<Ans) Ans=Need[0][i]+Need[1][i]+Line[i];
}
cout<<Ans<<'\n';
}
return 0;
}
如果觉得题解对你有帮助,那就点个赞吧。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探