P5025 [SNOI2017] 炸弹 题解

news/2024/10/18 13:38:08/文章来源:https://www.cnblogs.com/zphh/p/18474052

题意

link.

题解

一个好想一点的正解。


考虑到可能连锁爆炸,我们不能通过一个单纯的二分来解决问题。

考虑 dp。

\(f(i)\) 为第 \(i\) 个点爆炸,最远能引爆到哪个坐标小于它的点。

\(g(i)\) 为第 \(i\) 个点爆炸,最远能引爆到哪个坐标大于它的点。

我们以 \(f\) 为例,\(g\) 可以通过同样的方法求得。

对于每一个 \(i\),二分出一个最小的 \(l\) 满足 \(\lvert x_l - x_i \rvert \le r_i\),此时区间 \(\left [ l, i - 1 \right ]\) 就是 \(i\) 通过一次爆炸能爆到的。

但是可以连锁爆炸,所以我们需要选出一个 \(t \in \left [ l, i - 1 \right ]\) 使得 \(f(t)\) 最小。

此时就可以转移了:

\[f(i) = \min \{ f(t), l\} \]

但找出 \(t\) 的复杂度是 \(\mathcal O(n)\) 的,总时间复杂度是 \(\mathcal O (n^2 + n \log n)\) 的,这样就可以光荣的获得 \(55\) 分了。

考虑开一棵线段树,维护 \(x_i - r_i\) 的最小值和最小值的编号。

因为满足 \(f(t)\) 最小的 \(t\) 也一定满足 \(x_i - r_i\) 最小,反之同理。

此时只需要查询一下区间 \(\left [ l, i - 1 \right ]\) 最小值编号即可作为 \(t\) 转移了。

\(g\) 只需要开一棵最大值线段树即可。


然后你就会发现它错了(貌似样例都过不了)。

我们不能把思维固定在 \(f\) 转移 \(f\)\(g\) 转移 \(g\) 上。

有可能我们先爆炸炸到 \(i\) 右边,然后通过引爆 \(i\) 右边一枚非常强悍的炸弹然后再引爆到左边。

所以要反着再转移一次。


然后你就会发现它又错了(貌似会错第二个点)。

因为我们通过走右边到达了左边,此时我们还需要再走一次左边,因为走过来不一定是最小的(真烦)。

右边同理。

所以重复一边上文的过程即可。


但值得庆幸的是,这么就能过了,因为此时已经把所有方案考虑完了。

时间复杂度:\(\mathcal O (n \log n)\),带一点线段树的并不小的常数。

namespace zqh {
const int N = 5e5 + 5;int n;
struct bomb {  // 炸弹结构体int x, r;
} a[N];
int dpl[N], dpr[N];
struct segment {    // 线段int mx, mx_id;  // 最大值,最大值编号,下同int mn, mn_id;
};struct segment_tree {  // 线段树
#define ls (id << 1)
#define rs (id << 1 | 1)segment seg[N << 2];void pushup(int id) {if (seg[ls].mn < seg[rs].mn) {seg[id].mn = seg[ls].mn;seg[id].mn_id = seg[ls].mn_id;} else {seg[id].mn = seg[rs].mn;seg[id].mn_id = seg[rs].mn_id;}if (seg[ls].mx > seg[rs].mx) {seg[id].mx = seg[ls].mx;seg[id].mx_id = seg[ls].mx_id;} else {seg[id].mx = seg[rs].mx;seg[id].mx_id = seg[rs].mx_id;}}void build(int id, int lft, int rht) {  // 建树seg[id].mn = LLONG_MAX;seg[id].mx = LLONG_MIN;if (lft == rht) {seg[id].mn_id = seg[id].mx_id = lft;seg[id].mn = a[lft].x - a[lft].r;seg[id].mx = a[lft].x + a[lft].r;return;}int mid = (lft + rht) / 2;build(ls, lft, mid);build(rs, mid + 1, rht);pushup(id);}segment query(int id, int lft, int rht, int l, int r) {if (rht < l || r < lft)return {LLONG_MIN, -1, LLONG_MAX, -1};if (l <= lft && rht <= r)return seg[id];int mid = (lft + rht) / 2;segment t1 = query(ls, lft, mid, l, r),t2 = query(rs, mid + 1, rht, l, r), ret;if (t1.mn < t2.mn) {ret.mn = t1.mn;ret.mn_id = t1.mn_id;} else {ret.mn = t2.mn;ret.mn_id = t2.mn_id;}if (t1.mx > t2.mx) {ret.mx = t1.mx;ret.mx_id = t1.mx_id;} else {ret.mx = t2.mx;ret.mx_id = t2.mx_id;}return ret;}
} st;void dp() {                         // 跑一次 dpfor (int i = 2; i <= n; i++) {  // 算 f(直接走左边)int l = 1, r = i - 1, ans = -1;while (l <= r) {int mid = (l + r) / 2;if (abs(a[mid].x - a[i].x) <= a[i].r) {ans = mid;r = mid - 1;} else {l = mid + 1;}}if (ans == -1)continue;segment t = st.query(1, 1, n, ans, i - 1);dpl[i] = min(dpl[i],min((dpl[t.mn_id] == -1 ? LLONG_MAX : dpl[t.mn_id]), ans));}for (int i = n - 1; i >= 1; i--) {  // 算 g(直接走右边)int l = i + 1, r = n, ans = -1;while (l <= r) {int mid = (l + r) / 2;if (abs(a[mid].x - a[i].x) <= a[i].r) {ans = mid;l = mid + 1;} else {r = mid - 1;}}if (ans == -1)continue;segment t = st.query(1, 1, n, i + 1, ans);dpr[i] = max(dpr[i],max((dpr[t.mx_id] == -1 ? LLONG_MIN : dpr[t.mx_id]), ans));}for (int i = 2; i <= n; i++) {  // 算 f(走右边再走左边)int l = 1, r = i - 1, ans = -1;while (l <= r) {int mid = (l + r) / 2;if (abs(a[mid].x - a[i].x) <= a[i].r) {ans = mid;r = mid - 1;} else {l = mid + 1;}}if (ans == -1)continue;segment t = st.query(1, 1, n, ans, i - 1);dpr[i] = max(dpr[i],max((dpr[t.mx_id] == -1 ? LLONG_MIN : dpr[t.mx_id]), ans));}for (int i = n - 1; i >= 1; i--) {  // 算 g(走左边再走右边)int l = i + 1, r = n, ans = -1;while (l <= r) {int mid = (l + r) / 2;if (abs(a[mid].x - a[i].x) <= a[i].r) {ans = mid;l = mid + 1;} else {r = mid - 1;}}if (ans == -1)continue;segment t = st.query(1, 1, n, i + 1, ans);dpl[i] = min(dpl[i],min((dpl[t.mn_id] == -1 ? LLONG_MAX : dpl[t.mn_id]), ans));}
}void init() {cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i].x >> a[i].r;dpl[i] = dpr[i] = i;}
}void solve() {st.build(1, 1, n);  // 建树dp();               // 跑两次dp();int ans = 0;for (int i = 1; i <= n; i++) {  // 统计答案//			cout << dpl[i] << " " << dpr[i] << endl;if (dpl[i] == -1 && dpr[i] == -1) {ans = 1LL * (ans + 1 * i) % mod;continue;}if (dpl[i] == -1) {ans = (ans + 1LL * (1LL * (dpr[i] - i + 1) % mod * i) % mod) % mod;continue;}if (dpr[i] == -1) {ans = (ans + 1LL * (1LL * (i - dpl[i] + 1) % mod * i) % mod) % mod;continue;}ans = (ans + 1LL * (1LL * (dpr[i] - dpl[i] + 1) % mod * i) % mod) % mod;}cout << ans;
}void main() {init();solve();
}
}  // namespace zqh

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/816828.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2153: 【例8.3】计算球的体积 球的体积公式

include <bits/stdc++.h> using namespace std; double r, pi=3.14; int main( ) { cin >> r; cout << fixed << setprecision(2)<< 4.0/3.0pirrr; return 0; } 球体是一个半圆绕直径所在直线旋转一周所成的空间几何体,简称球。球体是有且只有一…

深入理解浮点数的运算

浮点数的运算步骤 浮点数的加减运算一般由以下五个步骤完成:对阶、尾数运算、规格化、舍入处理、溢出判断 所谓对阶是指将两个进行运算的浮点数的阶码对齐的操作。对阶的目的是为使两个浮点数的尾数能够进行加减运算。因为,当进行 $ M_{x} \times 2^{E_{x}}$与 $ M_{y} \time…

轻松上手-识图文字朗读

作者:狼哥 团队:坚果派 团队介绍:坚果派由坚果等人创建,团队拥有12个华为HDE带领热爱HarmonyOS/OpenHarmony的开发者,以及若干其他领域的三十余位万粉博主运营。专注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服务、仓颉。团队成员聚集在北京,上海,南京,深圳,广州,宁…

mysql语法-DMLDQL

1.DML操作数据——添加、修改、删除 (1)添加数据:实例(2)修改数据实例注意:修改时如果update语句不加where条件,则会把表中所有数据都修改了! (3)删除数据:实例2.DQL查询 查询语法(1)基础查询:实例(2)条件查询:

免费使用AI写作助手,为你轻松打造爆款文章

在当今内容为王的时代,一篇高质量的文章能够迅速抓住读者的眼球,提升个人或品牌的曝光度。但对于许多创作者而言,灵感枯竭和写作效率低下是常见的挑战。此时,免费AI写作助手的出现,为解决这些问题提供了新的可能性。以下是这款AI写作助手的独特魅力和使用指南。一、AI写作…

从组合优化问题建模到贪心法求解以简单调度为例

此为课题组所指导本科生和低年级硕士生学习组合优化问题汇报 所用教材:北京大学屈婉玲教授《算法设计与分析》 课程资料:https://www.icourse163.org/course/PKU-1002525003 承诺不用于任何商业用途,仅用于学术交流和分享更多内容请关注课题组官方中文主页:https://JaywayX…

python: invalid value encountered in divide以及invalid value encountered in double_scalars报错

运行命令python eqtl_prepare_expression.py data.tpm.gct data.reads_count.gct --tpm_threshold 0.1 --count_threshold 2 --sample_frac_threshold 0.2 --normalization_method tmm --output data.txt时出现了报错“invalid value encountered in divide”以及“invalid val…

java报错大合集

​D:\代码\Mybatis-84\src\test\java\com\lu\TestNews.java:100:39 java: 找不到符号符号: 方法 of(int,int)位置: 接口 java.util.List解决idea中的jdk变成1..8了而List.of()是9出的所有报错,改回17 在“class java.lang.String”中没有名为“name”的属性的 getter纯属粗心…

DataDream:调一调更好,基于LoRA微调SD的训练集合成新方案 | ECCV24

尽管文本到图像的扩散模型已被证明在图像合成方面达到了最先进的结果,但它们尚未证明在下游应用中的有效性。先前的研究提出了在有限的真实数据访问下为图像分类器训练生成数据的方法。然而,这些方法在生成内部分布图像或描绘细粒度特征方面存在困难,从而阻碍了在合成数据集…

深入理解浮点数的表示

浮点数的表示 通常,浮点数表示为: \[N = (-1)^{S} \times M \times R^{E} \]其中,S取值为0或者1,用来决定浮点数的符号;M是一个二进制定点小数,称为尾数,一般用定点原码小数表示;E是一个二进制顶点整数,称为阶码或者指数,用移码表示。R是基数(隐含),可以约定为2、4、…

20222410 2024-2025-1 《网络与系统攻防技术》实验三实验报告

1.实验内容正确使用msf编码器,veil-evasion,自己利用shellcode编程等免杀工具或技巧正确使用msf编码器,使用msfvenom生成如jar之类的其他文件 veil,加壳工具 使用C + shellcode编程通过组合应用各种技术实现恶意代码免杀 如果成功实现了免杀的,简单语言描述原理,不要截图…