Snuketoon [ABC217H]
https://atcoder.jp/contests/abc217/tasks/abc217_h
题解
设 表示在 时刻在 位置,受到的最小伤害是多少
记
以 为例,转移有
观察一下样例可以发现,对于一个固定的 ,若把所有 当成点画在二维平面上并连接相邻点,那么图像会是一段下凸壳,也就是说 是一个下凸的函数
不妨把上面的转移分成两步,先是
此时对于凸壳左半边斜率小于 的部分,有 ,所以
同理对于右半边斜率大于 的部分,有
所以这一步就相当于将凸壳左半边向左平移 ,右半边向右平移
第二步是给每个 加上 (仍然以 为例)
可以发现,这个操作相当于给 这段的 加上一个斜率为 的一次函数
考虑怎么维护这个凸壳:
对于左半边斜率小于0的部分,从右到左维护若干个断点 ,表示 区间上凸壳斜率为 , 上斜率为 ,......
对于右半边同理;额外维护一个 表示斜率为 部分的答案
对于第一步转移,直接维护左半边和右半边的偏移量即可
对于第二步转移,以 为例,设右半边凸壳的第一个断点为
如果 ,那么此时 左边的部分斜率要全部 ,所以将 作为断点加入左半边凸壳的断点集合即可
如果 ,那么有一段原来斜率为 的区间加上这个斜率为 的一次函数后斜率变成 了,更新 ,同时将右半边第一个断点改为 ,左半边新增了断点
下面放一张图方便理解:
时也同理
最后答案即为
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
priority_queue<ll> q0;
priority_queue<ll, vector<ll> , greater<ll> > q1;
int main() {
scanf("%d", &n); ll ans = 0;
for (int i = 1, t, d, x; i <= n; i++) {
scanf("%d %d %d", &t, &d, &x);
if (d == 0) {
if (x > t) ans += x-t, x = t;
if (q1.empty() || x <= q1.top()+t) q0.push(x+t);
else {
ll y = q1.top()+t;
ans += x-y;
q0.push(y+t);
q1.pop(); q1.push(x-t);
}
} else {
if (x < -t) ans += -t-x, x = -t;
if (q0.empty() || x >= q0.top()-t) q1.push(x-t);
else {
ll y = q0.top()-t;
ans += y-x;
q0.pop(); q0.push(x+t);
q1.push(y-t);
}
}
}
printf("%lld\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现