火烧赤壁题解
题目传送门:P1496 火烧赤壁
一、题目描述
给定n个着火区间,每个区间表示为[a,b)(左闭右开),要求计算所有着火区间覆盖的总长度(合并重叠或相邻的区间)。
二、题目分析
我们需要处理多个区间,合并其中重叠或相邻的部分,然后计算合并后各区间的长度之和。关键在于如何高效地合并这些区间。
三、解题思路 + 算法介绍
- 区间合并算法:
- 首先将所有区间按照左端点排序
- 然后遍历排序后的区间,维护当前合并区间的起始点(st)和结束点(ed)
- 如果当前区间的左端点大于维护区间的右端点,说明不重叠,将维护区间加入结果
- 否则,更新维护区间的右端点为两者中的较大值
- 计算总长度:
- 合并完成后,遍历所有合并后的区间,累加每个区间的长度(b-a)
四、代码实现
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
int n;
vector<PII> sect;
// 合并重叠或相邻的区间
void merge(vector<PII> §)
{
vector<PII> res;
int st = -1e10, ed = -1e10; // 初始化为极小值
sort(sect.begin(), sect.end()); // 按左端点排序
for (auto seg : sect)
{
if (ed < seg.first) // 当前区间与维护区间无重叠
{
if (st != -1e10) // 不是初始状态,将维护区间加入结果
{
res.push_back({st, ed});
}
st = seg.first, ed = seg.second; // 开始维护新区间
}
else // 有重叠,合并区间
ed = max(ed, seg.second);
}
// 加入最后一个维护的区间
if (st != -1e10)
{
res.push_back({st, ed});
}
sect = res;
}
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
int a, b;
cin >> a >> b;
sect.push_back({a, b}); // 存储所有区间
}
merge(sect); // 合并区间
int res = 0;
for (auto seg : sect) // 计算总长度
{
res += seg.second - seg.first;
}
cout << res << endl;
return 0;
}
五、重点细节
- 区间表示:题目中的区间是左闭右开[a,b),所以长度直接计算为b-a
- 初始化值:合并算法中初始st和ed设为极小值(-1e10),用于判断是否是第一个区间
- 排序处理:必须先对所有区间按左端点排序,才能正确合并
- 边界处理:最后一个合并后的区间需要在循环结束后单独加入结果集
六、复杂度分析
- 时间复杂度:
- 排序操作:O(n log n)
- 合并操作:O(n)
- 总复杂度:O(n log n)(排序占主导)
- 空间复杂度:
- 存储区间:O(n)
- 合并结果:最坏情况O(n)(无任何区间可合并时)
七、总结
本题考察了区间合并这一经典算法,关键在于:
- 理解如何判断和处理区间重叠
- 掌握排序预处理的重要性
- 注意边界条件的处理(如最后一个区间)
- 理解左闭右开区间的长度计算方式
该解法高效且清晰,能够正确处理题目中的所有情况。