本题存在诸如线段树上做离散化等高级做法,但由于线段树给人的印象很bad(wch说的),所以我们模拟解决这道题。
先把输进来的线段按照左端小在前,左端相同时按右端小在前的原则进行排序,可以证明这样排序答案必定正确,显然。
对所有线段进行区间合并(参考y总算法基础课第一章基础算法中的“区间合并”),对于合并后的每个线段统计长度之和。
注意:数据中的线段左闭右开。
// Problem: P1496 火烧赤壁
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1496
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <cstdio> // 包含标准输入输出函数库
#include <algorithm> // 包含排序等算法函数库
#include <vector> // 包含向量容器类库
using namespace std; // 引入标准命名空间// 定义一个别名PII,表示成对的整数(用于存储区间的起点和终点)
typedef pair<int, int> PII;
// 定义一个向量容器segs,用于存储所有的着火区间信息
vector<PII> segs;
// 定义整数n,用于存储着火信息的条数
int n;//合并重叠或相邻的区间
void merge(vector<PII> &segs) {// 先将所有区间按照起点从小到大排序sort(segs.begin(), segs.end());// 定义一个临时向量res,用于存储合并后的区间vector<PII> res;// 初始化当前区间的起点st和终点ed(使用一个极小值作为初始值)int st = -2e9, ed = -2e9;// 遍历每一个区间for (auto seg : segs) {// 取出当前区间的起点l和终点rint l = seg.first, r = seg.second;// 如果当前区间的起点大于上一个区间的终点,说明两个区间不重叠if (ed < l) {// 如果不是初始区间(避免添加默认的极小值区间),则将上一个区间加入结果if (st != -2e9) res.push_back({st, ed});// 更新当前区间为新的区间st = l, ed = r;} else {// 如果区间重叠或相邻,则合并区间(更新终点为两个区间终点的最大值)ed = max(ed, r);}}// 循环结束后,将最后一个区间加入结果(避免遗漏)if (st != -2e9) res.push_back({st, ed});// 将合并后的结果赋值给原向量segs = res;
}int main() {// 读取着火信息的条数nscanf("%d", &n);// 循环读取n条着火区间信息for (int i = 1; i <= n; i++) {int l, r;// 读取区间的起点l和终点r(题目中是左闭右开区间)scanf("%d%d", &l, &r);// 将左闭右开区间转换为左闭右闭区间(方便计算长度),并加入到segs中// 原区间[a, b)等价于[a, b-1](闭区间)segs.push_back({l, r - 1});}// 调用merge函数合并重叠的区间merge(segs);// 定义变量ans,用于存储最终的燃烧总长度long long ans = 0;// 遍历合并后的所有区间,计算总长度for (auto seg : segs) {// 对于左闭右闭区间[st, ed],长度为ed - st + 1ans += (long long)seg.second + 1 - seg.first;}// 输出计算得到的总长度printf("%lld\n", ans);return 0;
}
