1 条题解
-
0
题解:ROI 2024 Day1「拉马赞与卷心菜」
问题分析
题目要求统计所有满足条件的水平线段 ,其中:
- 线段上所有点 ()都属于种植区
- 线段左右相邻点 和 都不属于种植区
我们需要统计:
- 所有不同的 对
- 每个 对出现的行数
- 每个 对连续出现的最大行数
算法思路
核心观察
水平线段 满足条件当且仅当:
- 在 行上,区间 完全被覆盖(种植卷心菜)
- 在 行上,点 和 未被覆盖
扫描线算法
使用垂直扫描线从左到右扫描 坐标,同时维护当前 轴上的覆盖情况。
数据结构设计
- 离散化:将 坐标离散化,建立线段树
- 线段树:维护每个 区间上的覆盖次数和连续段信息
线段树维护的信息
对于每个节点维护:
mncnt:最小覆盖次数appcnt:达到最小覆盖次数的区间总长度mnl,mxl:最小/最大 值(用于标识连续段)lft,rgt:左右连续段长度seg:中间最大连续段长度totl:节点对应区间总长度
关键操作
-
更新操作:处理矩形的加入和删除
- 加入矩形:对应 区间覆盖次数+1,记录当前 坐标
- 删除矩形:对应 区间覆盖次数-1
-
查询操作:在扫描线移动时,查询当前所有覆盖次数为0的连续段
- 这些连续段对应有效的水平线段
- 记录 对的出现情况和连续性
代码实现详解
数据结构初始化
void build(int k, int l, int r, int *x) { // 初始化线段树节点 mncnt[k] = appcnt[k] = mnl[k] = mxl[k] = lft[k] = rgt[k] = seg[k] = 0; totl[k] = ad[k] = 0; laz[k] = -1; if (l == r) { // 叶子节点:区间长度为离散化后相邻y坐标的差值 mncnt[k] = 0; appcnt[k] = totl[k] = lft[k] = rgt[k] = seg[k] = x[l+1] - x[l]; mnl[k] = mxl[k] = 0; } else { // 递归构建左右子树 int mid = (l + r) >> 1; build(k*2, l, mid, x); build(k*2+1, mid+1, r, x); pushup(k); // 合并子节点信息 } }扫描线处理
for (int i = 1; i <= lenx; i++) { // 处理当前x坐标的所有矩形起始边 for (auto j : upds[i]) { update(1, 1, len-1, j.first, j.second, xs[i], 1); } // 处理当前x坐标的所有矩形结束边 for (auto j : dels[i]) { update(1, 1, len-1, j.first, j.second, -1, -1); } // 查询当前有效的水平线段 query(1, 1, len-1, xs[i], ys); // 清理标记 for (auto j : dels[i]) { update(1, 1, len-1, j.first, j.second, 0, 0); } }有效线段识别
在查询过程中,当发现覆盖次数为0的连续段时:
- 这些段对应有效的水平线段
- 其中 和 由线段树节点维护的边界信息确定
- 统计出现次数和最大连续长度
复杂度分析
- 时间复杂度:,其中 是所有测试用例中矩形数量的总和
- 空间复杂度:,用于存储离散化坐标和线段树
样例解析
以第一组样例为例:
2 2 2 3 3 3 3 4 4种植区形成的有效水平线段:
- :在y=2行,区间[2,3]被覆盖,且左右相邻点未被覆盖
- :在y=3行
- :在y=4行
因此输出3个不同的 对,每个出现1次,最大连续长度1。
总结
本题通过扫描线算法结合线段树维护y轴覆盖情况,高效地统计了所有满足条件的水平线段。关键点在于:
- 利用离散化处理大坐标范围
- 线段树维护覆盖信息和连续段特性
- 扫描线按x坐标顺序处理矩形边界
- 在覆盖次数变化时识别有效的水平线段
该算法能够处理 的大规模数据,在时间和空间复杂度上达到最优。
- 1
信息
- ID
- 4772
- 时间
- 4000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者