Luogu5490 【模板】扫描线(矩形的面积并)

本文介绍了一种使用扫描线算法解决多个矩形面积并的问题的方法。通过将二维问题转化为一维问题,利用线段树维护区间覆盖状态,实现高效求解。适用于计算机图形学与算法竞赛。

原题链接:https://www.luogu.com.cn/problem/P5490

【模板】扫描线

题目描述

n n n 个矩形的面积并。

输入格式

第一行一个正整数 n n n

接下来 n n n 行每行四个非负整数 x 1 , y 1 , x 2 , y 2 x_1, y_1, x_2, y_2 x1,y1,x2,y2 ,表示一个矩形的左下角坐标为 ( x 1 , y 1 ) (x_1, y_1) (x1,y1),右上角坐标为 ( x 2 , y 2 ) (x_2, y_2) (x2,y2)

输出格式

一行一个正整数,表示 n n n 个矩形的并集覆盖的总面积。

输入输出样例

输入 #1
2
100 100 200 200
150 150 250 255
输出 #1
18000

说明/提示

对于 20 % 20\% 20% 的数据, 1 ≤ n ≤ 1000 1 \le n \le 1000 1n1000

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 6 , 0 ≤ x 1 < x 2 ≤ 1 0 9 , 0 ≤ y 1 < y 2 ≤ 1 0 9 1 \le n \le 10^6, 0 \le x_1 < x_2 \le 10^9, 0 \le y_1 < y_2 \le 10^9 1n106,0x1<x2109,0y1<y2109

题解

扫描线的模板题,然而原网页上的数据范围写的 n ≤ 1 0 5 n\le 10^5 n105,实际数据范围 1 0 6 10^6 106就™离谱。

扫描线可以说是一种线段树的运用,也可以说是一种思想:在处理二维的问题时,可以先解决其中一维的问题,再沿着另一维拓展延伸。

对于求矩形面积并的问题,从多维视角上看,矩形面积实际上是一维的线段沿着另一维移动扫过的面积。这个问题转换到一维就是求出每个时刻原图形的截线长度,下图黄线所示就是若干时刻的截线:
在这里插入图片描述
更进一步,我们可以发现截线的长度仅在经过长方形的边时发生改变,所以有用的截线实际上只有这几条:
在这里插入图片描述
而截线长度的变化就是因为添加/删去了矩形的边,所以我们把矩形拆成上边和下边,从下往上遍历每个边,遇到下边时对覆盖的区间加一,遇到上边则对覆盖的区间减一;每完成一次更新,就乘以相邻边的高度差,如此就能得到矩形并的面积。

要完成区间加和整体求和的操作,只需要在离散化横坐标的基础上建立线段树维护区间被覆盖次数 c o v cov cov和本区间内被覆盖的总长度 l e n len len

代码

由于横坐标划分的区间是在实数域上的,所以在边界处理上与区间为整数域的线段树有所不同:

#include<bits/stdc++.h>
#define ls v<<1
#define rs v<<1|1
using namespace std;
const int M=1e6+5;
struct Edge{
    int le,ri,h,val;
}edge[M<<1];
bool cmp(Edge a,Edge b){return a.h<b.h;}
struct node{
    int le,ri,len,cov;
}tree[M<<2];
int n,tot,x[M],cot;
long long ans;
void up(int v){tree[v].len=tree[v].cov?tree[v].ri-tree[v].le:tree[ls].len+tree[rs].len;}
void build(int v,int le,int ri)
{
    tree[v].le=x[le],tree[v].ri=x[ri+1];
    if(le==ri)return;
    int mid=le+ri>>1;
    build(ls,le,mid),build(rs,mid+1,ri);
}
void cover(int v,int le,int ri,int val)
{
    if(le<=tree[v].le&&tree[v].ri<=ri)
    {
        tree[v].cov+=val;up(v);
        return;
    }
    if(le<tree[ls].ri)cover(ls,le,ri,val);
    if(tree[rs].le<ri)cover(rs,le,ri,val);
    up(v);
}
void in()
{
    scanf("%d",&n);
    for(int i=1,a,b,c,d;i<=n;++i)
    scanf("%d%d%d%d",&a,&b,&c,&d),edge[++cot]=(Edge){a,c,b,1},edge[++cot]=(Edge){a,c,d,-1},x[++tot]=a,x[++tot]=c;
}
void ac()
{
    sort(x+1,x+1+tot);
    tot=unique(x+1,x+1+tot)-x-1;
    build(1,1,tot-1);
    sort(edge+1,edge+1+cot,cmp);
    for(int i=1;i<=cot;++i)
    {
        ans+=1ll*tree[1].len*(edge[i].h-edge[i-1].h);
        cover(1,edge[i].le,edge[i].ri,edge[i].val);
    }
    printf("%lld\n",ans);
}
int main()
{
    in(),ac();
    //system("pause");
}
(2)(矩形覆盖问题)在平面直角坐标系上有 𝑛 个矩形矩形的每条边与坐标系的横轴平 行或垂直。对于第 𝑖 个矩形矩形左下角的顶点坐标为 (𝑠𝑥, 𝑠𝑦),右上角的顶点坐标为 (𝑒𝑥,𝑒𝑦)。每个矩形要么严格包含另一个矩形,要么完全不相交。矩形的边界不会相交、顶 点或边也不会重合。要求,对于每个矩形,分别求出包含它的矩形中最小的矩形。如果没有 被覆盖,则输出 0。上述参数满足 1 ≤ 𝑛 ≤ 2 × 105,1 ≤ 𝑠𝑥, 𝑠𝑦,𝑒𝑥,𝑒𝑦 ≤ 109,你需要设计一 个时间复杂度不超过 Θ(𝑛 log 𝑛) 的程序。试补全程序。 1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <utility> 5 using namespace std; 6 7 typedef long long ll; 8 typedef pair<ll, ll> pii; 9 10 struct rectangle { ll sx, sy, ex, ey; 11 12 }; 13 14 struct line { ll x, sy, ey, id; 15 friend bool operator < (const line &lhs, const line &rhs) { 16 return ①; 17 } 18 19 }; 20 21 struct segmentTree { vector<int> tree, lazy; 22 23 LUOGU SCP-S 2025 第一轮 C++语言试题 第 14 页,共 17 页 static int ls(int u) { return u << 1; } 24 static int rs(int u) { return u << 1 | 1; } 25 segmentTree(int N): tree(N * 4 + 1, 0), lazy(N * 4 + 1, -1) {} 26 27 void pushdown(int p) { 28 if(~lazy[p]) { 29 tree[ls(p)] = tree[rs(p)] = lazy[p]; 30 lazy[ls(p)] = lazy[rs(p)] = lazy[p]; 31 lazy[p] = -1; 32 } 33 } 34 35 void update(int p, int l, int r, int ql, int qr, int k) { 36 if (l >= ql && r <= qr) { 37 tree[p] = lazy[p] = k; 38 return; 39 } 40 pushdown(p); int mid = (l + r) >> 1; 41 if () update(ls(p), l, mid, ql, qr, k); 42 if (qr > mid) update(rs(p), mid + 1, r, ql, qr, k); 43 } 44 45 int query(int p, int l, int r, int x) { 46 if (l == r) return tree[p]; 47 pushdown(p); int mid = (l + r) >> 1; 48 if (mid >= x) return query(ls(p), l, mid, x); 49 else return query(rs(p), mid + 1, r, x); 50 } 51 52 }; 53 54 template<class T> struct discretize { vector<T> data; 55 discretize(): data() {} 56 57 void insert(const T &x) { data.push_back(x); } 58 LUOGU SCP-S 2025 第一轮 C++语言试题 第 15 页,共 17 页 59 void build() { 60 sort(data.begin(), data.end()); 61 auto endp = unique(data.begin(), data.end()); 62 data.erase(endp, data.end()); 63 } 64 65 size_t size() const { return data.size(); } 66 67 int operator[](const T &x) { 68 return ③; 69 } 70 71 }; 72 73 int main() { int n; cin >> n; 74 vector<rectangle> P(n); 75 for (int i = 0; i < n; i++) 76 cin >> P[i].sx >> P[i].sy >> P[i].ex >> P[i].ey; 77 discretize<ll> discx, discy; 78 for (int i = 0; i < n; i++) { 79 discx.insert(P[i].sx); 80 discx.insert(P[i].ex); 81 discy.insert(P[i].sy); 82 discy.insert(P[i].ey); 83 } 84 discx.build(); discy.build(); 85 for (int i = 0; i < n; i++) { 86 P[i].sx = discx[P[i].sx] + 1; 87 P[i].ex = discx[P[i].ex] + 1; 88 P[i].sy = discy[P[i].sy] + 1; 89 P[i].ey = discy[P[i].ey] + 1; 90 } 91 vector<line> L; int cnt = 0; 92 for(int i = 0; i < n; i++) { 93 LUOGU SCP-S 2025 第一轮 C++语言试题 第 16 页,共 17 页 L.push_back({P[i].sx, P[i].sy, P[i].ey, ++cnt}); 94 L.push_back({④}); 95 } 96 sort(L.begin(), L.end()); 97 int N = discx.size(); 98 segmentTree S(N); 99 vector<int> fa(n + 1, -1); 100 for (int i = 0; i < L.size(); i++) { 101 int c = S.query(1, 1, N, L[i].sy); 102 if (L[i].id < 0) 103 S.update(1, 1, N, L[i].sy, L[i].ey, fa[-L[i].id]); 104 else { 105 fa[L[i].id] = c; 106 ⑤ 107 } 108 } 109 for (int i = 1; i <= cnt; ++i) 110 cout << fa[i] << " \n"[i == cnt]; 111 return 0; 112 113 } 38. ①处应填( ) A. lhs.sy < rhs.sy || lhs.ey < rhs.ey B. lhs.sy == rhs.sy? lhs.id < rhs.id : lhs.sy < rhs.sy C. lhs.ey == rhs.ey ? lhs.id < rhs.id : lhs.ey < rhs.ey D. lhs.x == rhs.x ? lhs.id < rhs.id : lhs.x < rhs.x 39. ②处应填( ) A. ql < mid B. ql <= mid C. ql >= mid D. ql == mid 40. ③处应填( ) A. lower_bound(data.begin(), data.end(), x) - data.begin() B. upper_bound(data.begin(), data.end(), x) - data.begin() C. find(data.begin(), data.end(), x) - data.begin() D. distance(data.end(), upper_bound(data.begin(), data.end(), x)) 41. ④处应填( ) A. P[i].sx, P[i].sy, P[i].ey, cnt B. P[i].sx, P[i].sy, P[i].ey, -cnt C. P[i].ex, P[i].sy, P[i].ey, cnt D. P[i].ex, P[i].sy, P[i].ey, -cnt 42. ⑤处应填( ) LUOGU SCP-S 2025 第一轮 C++语言试题 第 17 页,共 17 页 A. fa[i] = S.query(1, 1, N, c) B. fa[i] = S.query(1, 1, N, L[i].id) C. S.update(1, 1, N, L[i].sy, L[i].ey, L[i].id) D. S.update(1, 1, N, L[i].sx, L[i].ex, L[i].id)
最新发布
08-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值