1. 程式人生 > 其它 >Luogu5490 【模板】掃描線(矩形的面積並)

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");
}