扫描线|离散化|seg+二分|卡常

lc3454

只统计一次_预处理: 扫描线+线段树计算正方形总面积

二分的扫描线遍历找到分割线位置,使分割线两侧面积趋近相等,最终求解分割线的纵坐标值

卡常代码: 常数时间过大导致超时

可用静态数组替代动态容器、去掉懒更新减少内存分配/函数调用等额外开销,降低运行常数以通过时间限制

constexpr int MAX_N = 100'000 + 5;

array<int, 4> y_posMAX_N;

int x_posMAX_N;

int x_diffMAX_N;

int total_lengthMAX_N \<\< 2;

int cover_lengthMAX_N \<\< 2;

int cover_timesMAX_N \<\< 2;

void up(int i) {

if (cover_timesi) {

cover_lengthi = total_lengthi;

} else {

cover_lengthi = cover_lengthi \* 2 + cover_lengthi \* 2 + 1;

}

}

void build(const int *arr, int i, int l, int r) {

cover_lengthi = cover_timesi = 0;

if (l == r) {

total_lengthi = arrl;

return;

}

int m = (l + r) / 2;

build(arr, i * 2, l, m);

build(arr, i * 2 + 1, m + 1, r);

total_lengthi = total_lengthi \* 2 + total_lengthi \* 2 + 1;

}

void update(int i, int l, int r, int ql, int qr, int v) {

if (ql <= l && r <= qr) {

cover_timesi += v;

if (l != r) {

up(i);

} else {

cover_lengthi = cover_timesi ? total_lengthi : 0;

}

return;

}

int m = (l + r) / 2;

if (ql <= m) {

update(i * 2, l, m, ql, qr, v);

}

if (qr > m) {

update(i * 2 + 1, m + 1, r, ql, qr, v);

}

up(i);

}

class Solution {

public:

double separateSquares(vector<vector<int>>& squares) {

int n = squares.size();

for (int i = 0; i < n; ++i) {

auto &sq = squaresi;

int x1 = sq0, y1 = sq1, l = sq2;

int x2 = x1 + l, y2 = y1 + l;

y_posi = {y1, x1, x2, 1};

y_posi + n = {y2, x1, x2, -1};

x_posi = x1;

x_posi + n = x2;

}

n <<= 1;

++//离散化++

++sort(y_pos, y_pos + n, \[\](const auto &x, const auto &y) {return x0 < y0;});++

sort(x_pos, x_pos + n);

int x_cnt = unique(x_pos, x_pos + n) - x_pos - 1;

for (int i = 0; i < x_cnt; ++i)

x_diffi = x_posi + 1 - x_posi;

++build(x_diff, 1, 0, x_cnt - 1);++

long long total_s = 0;

for (int i = 0, pre_y = 0; i < n; ) {

int y = y_posi0;

total_s += 1LL * (y - pre_y) * cover_length1;

for (; i < n && y_posi0 == y; ++i) {

auto _, x1, x2, d = y_posi;

int ql = lower_bound(x_pos, x_pos + x_cnt + 1, x1) - x_pos;

int qr = lower_bound(x_pos, x_pos + x_cnt + 1, x2) - x_pos - 1;

++update(1, 0, x_cnt - 1, ql, qr, d);++

}

pre_y = y;

}

++build(x_diff, 1, 0, x_cnt - 1);++

long long s = 0;

int pre_y = 0;

for (int i = 0; i < n; ) {

int y = y_posi0;

s += 1LL * (y - pre_y) * cover_length1;

if (s * 2 >= total_s) {

s -= 1LL * (y - pre_y) * cover_length1;

break;

}

for (; i < n && y_posi0 == y; ++i) {

auto _, x1, x2, d = y_posi;

int ql = lower_bound(x_pos, x_pos + x_cnt + 1, x1) - x_pos;

int qr = lower_bound(x_pos, x_pos + x_cnt + 1, x2) - x_pos - 1;

++update(1, 0, x_cnt - 1, ql, qr, d);++

}

pre_y = y;

}

++double ans = pre_y + (double) (total_s - 2 * s) / (cover_length1 * 2);++

return ans;

}

};

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

线段树+二分

#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 {

return x < t.x;

}

};

Edge eN \<\< 1;

ll yN \<\< 1; // 存储所有y坐标,用于离散化

int cnt;

// 线段树:维护当前扫描线覆盖的y轴区间有效长度

struct Node {

int l, r;

int cnt; // 区间被覆盖的次数

ll len; // 区间的有效长度(被覆盖时的长度)

} trN \<\< 3;

void build(int u, int l, int r) {

tru = {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 (tru.cnt) tru.len = ytr\[u.r + 1] - ytr\[u.l];

else if (tru.l == tru.r) tru.len = 0;

else tru.len = tru \<\< 1.len + tru \<\< 1 \| 1.len;

}

// 线段树区间更新:修改覆盖次数

void update(int u, int l, int r, int k) {

if (tru.l >= l && tru.r <= r) {

tru.cnt += k;

++pushup(u);++

return;

}

int mid = tru.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 = rect0, y1 = rect1, x2 = rect2, y2 = rect3;

// 1. 构建事件点:左边界是入边,右边界是出边

ecnt++ = Edge(x1, y1, y2, 1);

ecnt++ = Edge(x2, y1, y2, -1);

// 收集所有y坐标,用于后续离散化

ycnt - 2 = y1;

ycnt - 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); // 线段树的叶子节点对应yi到yi+1的区间

ll res = 0;

for (int i = 0; i < cnt; i++) {

// 相邻事件点的x差 × 当前有效长度 = 这一段的面积

if (i > 0) res += tr1.len * (ei.x - ei - 1.x);

// 二分查找y1,y2对应的离散化下标

++int l = lower_bound(y, y + m, ei.y1) - y;++

int r = lower_bound(y, y + m, ei.y2) - y;

// 更新线段树:覆盖区间l, r-1

update(1, l, r - 1, ei.k);

}

return res;

}

说明

  1. 离散化作用:把++大范围的 y 坐标映射到小范围的下标++ ,避免线段树空间爆炸,是处理大坐标的必备操作

  2. 事件点排序核心作用:保证扫描线从左到右依次处理,入边先于出边的规则能避免同一 x 位置先减后加导致的有效长度计算错误

相关推荐
徐小夕8 小时前
我们放弃了单Agent方案:HiCAD 3.0 用 Harness 做多Agent编排,把3D建模的准确率提升了30%
前端·算法·github
洛水水8 小时前
【力扣100题】88.多数元素
数据结构·算法·leetcode
Shan12058 小时前
无向图的Hierholzer算法流程(一)
算法
一切皆是因缘际会8 小时前
频域特征解构底层机理与双域融合鉴伪算法优化
人工智能·算法·ai·架构
Smilecoc8 小时前
决策树(三):剪枝
算法·决策树·剪枝
bIo7lyA8v8 小时前
算法性能建模的数值方法与误差分析的技术8
算法
Smilecoc8 小时前
决策树(四):决策树实战之鸢尾花分类
算法·决策树·分类
-Thinker8 小时前
【无标题】
java·开发语言·算法·图搜索
数据仓库搬砖人8 小时前
DBSCAN 原理深度解析:从聚类算法到风控团伙识别的实战指南
算法
凡人叶枫8 小时前
Effective C++ 条款24:若所有参数皆须要类型转换,请为此采用 non-member 函数
linux·前端·c++·算法·嵌入式开发