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
輸入輸出樣例
輸入 #1
2
100 100 200 200
150 150 250 255
輸出 #1
18000
說明/提示
對於 20 % 20\% 20% 的資料, 1 ≤ n ≤ 1000 1 \le n \le 1000 1≤n≤1000。
對於
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
1≤n≤106,0≤x1<x2≤109,0≤y1<
題解
掃描線的模板題,然而原網頁上的資料範圍寫的 n ≤ 1 0 5 n\le 10^5 n≤105,實際資料範圍 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");
}