news 2026/6/15 20:16:34

扫描线|离散化|线段树+二分

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
扫描线|离散化|线段树+二分

lc

扫描线模板(矩形面积并)

线段树+二分

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 2010;

// 边的事件结构体:存储扫描线的入边/出边信息
struct Edge {
ll x, y1, y2;
int k; //入边k=1(覆盖+1),出边k=-1(覆盖-1)
Edge() {}
Edge(ll x, ll y1, ll y2, int k) : x(x), y1(y1), y2(y2), k(k) {}
// 事件点排序规则:按x坐标升序,x相同则入边优先(保证先加后减,避免漏算)
bool operator<(const Edge& t) const {
returnx < t.x;
}
};

Edge e[N << 1];
ll y[N << 1]; // 存储所有y坐标,用于离散化
int cnt;

// 线段树:维护当前扫描线覆盖的y轴区间有效长度
struct Node {
int l, r;
int cnt; // 区间被覆盖的次数
ll len; // 区间的有效长度(被覆盖时的长度)
} tr[N << 3];

void build(int u, int l, int r) {
tr[u] = {l, r, 0, 0};
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}

// 向上更新:根据子节点和当前覆盖次数计算有效长度
void pushup(int u) {
if (tr[u].cnt) tr[u].len = y[tr[u].r + 1] - y[tr[u].l];
else if (tr[u].l == tr[u].r) tr[u].len = 0;
else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
}

// 线段树区间更新:修改覆盖次数
void update(int u, int l, int r, int k) {
if (tr[u].l >= l && tr[u].r <= r) {
tr[u].cnt += k;
pushup(u);
return;
}
int mid = tr[u].l + r >> 1;
if (l <= mid) update(u << 1, l, r, k);
if (r > mid) update(u << 1 | 1, l, r, k);
pushup(u);
}

// 计算n个矩形的面积并
ll rectangleArea(vector<vector<ll>>& rects) {
cnt = 0;
int n = rects.size();
for (auto& rect : rects) {
ll x1 = rect[0], y1 = rect[1], x2 = rect[2], y2 = rect[3];
// 1. 构建事件点:左边界是入边,右边界是出边
e[cnt++] = Edge(x1, y1, y2, 1);
e[cnt++] = Edge(x2, y1, y2, -1);
// 收集所有y坐标,用于后续离散化
y[cnt - 2] = y1;
y[cnt - 1] = y2;
}

// ========== 离散化代码 start
sort(y, y + cnt); // 排序y坐标
// 去重:得到离散化后的唯一y坐标数量
int m = unique(y, y + cnt) - y;
// ========== 离散化代码 end


sort(e, e + cnt); // 按x坐标升序排序事件点
// ========== 事件点排序

build(1, 0, m - 2); // 线段树的叶子节点对应y[i]到y[i+1]的区间
ll res = 0;
for (int i = 0; i < cnt; i++) {
// 相邻事件点的x差 × 当前有效长度 = 这一段的面积
if (i > 0) res += tr[1].len * (e[i].x - e[i - 1].x);
// 二分查找y1,y2对应的离散化下标
int l = lower_bound(y, y + m, e[i].y1) - y;
int r = lower_bound(y, y + m, e[i].y2) - y;
// 更新线段树:覆盖区间[l, r-1]
update(1, l, r - 1, e[i].k);
}
return res;
}

说明

1. 离散化作用:把大范围的 y 坐标映射到小范围的下标,避免线段树空间爆炸,是处理大坐标的必备操作
2. 事件点排序核心作用:保证扫描线从左到右依次处理,入边先于出边的规则能避免同一 x 位置先减后加导致的有效长度计算错误

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 13:19:26

国标麻将一抽胡

我将创建一个简单的国标麻将一抽胡游戏&#xff0c;玩家每次随机获得一个听牌牌型&#xff0c;然后从一组牌中抽取一张&#xff0c;看是否能胡牌。思路分析1. 随机生成各种国标麻将听牌牌型&#xff08;缺一张即可胡牌&#xff09;2. 显示当前牌型&#xff0c;其中一张牌为&quo…

作者头像 李华
网站建设 2026/6/15 11:21:00

python基于vue的党员党史研究学习考试管理系统django flask pycharm

目录系统架构与技术栈核心功能模块技术实现细节部署与扩展性开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;系统架构与技术栈 该系统采用前后端分离架构&#xff0c;前端基于Vue.js框架开发…

作者头像 李华
网站建设 2026/6/15 12:21:20

杭州场来了!全球首款 AI 主题桌游试玩会丨 RTE x 环球黑客松

睡不着&#xff1f;想恋爱&#xff1f;开车犯困&#xff1f;…… 都是聊天能解决的事儿&#xff01; 玩家在 《Talk With》 里会面临抽到的 随机场景&#xff0c;每个场景都潜含着困难和危机。 你需要竞拍和挑选合适的 对话式 AI 和语音技术&#xff0c; 并给出有表现力的解决方…

作者头像 李华
网站建设 2026/6/15 13:16:23

手把手搞定毕设:基于YOLOv8从0到1构建检测系统,全流程详解(环境→训练→部署→答辩)

文章目录 基于YOLOv8的智能物体检测系统:毕设实战全流程,从搭建到上线,一路陪你跑通 第一步:环境搭建,别让基础坑了你后劲 第二步:数据集准备,喂对“食”才能长壮 第三步:模型训练,调参如炼丹,稳扎稳打出金 第四步:验证与推理,让模型“睁眼”看世界 第五步:优化导…

作者头像 李华
网站建设 2026/6/15 12:21:23

通透理解Mamba:三步实现从理论推导到手撕代码,一行不落的实战保姆指南

文章目录 从0玩转Mamba神经网络:理论+实战的保姆级教程 一、Mamba:序列任务的“效率新贵” 二、核心原理:Mamba的“制胜逻辑” 1. 状态空间模型(SSM)的魔力 2. Mamba vs Transformer:核心差异 三、实战入门:搭建简易Mamba模型 1. 环境搭建 2. 代码实现:Mamba文本分类器…

作者头像 李华
网站建设 2026/6/15 11:21:49

三步完成毕设:用YOLOv5实现艺术品识别(从数据采集到模型部署,轻松落地与展示)

文章目录 《深度学习实战:基于YOLOv5的公共艺术展艺术品识别毕设全指南》 一、为什么选择“公共艺术展艺术品识别”做毕设? 二、第一步:搞懂艺术品识别的“技术逻辑” 三、第二步:数据准备,给模型“喂”足艺术样本 1. 数据集从哪来? 2. 标注数据:给艺术品“画框框” 3. …

作者头像 李华