【面积并】 Atlantis
传送门
题意
多组测试样例,n表示矩形的个数,每个矩形用左上角和右下角来描述,输出这些矩形的面积并,
数据范围
\(\begin{array}{l}1 \leq n \leq 10000 \\ 0 \leq x_{1}<x_{2} \leq 100000 \\ 0 \leq y_{1}<y_{2} \leq 100000\end{array}\)
题解
一根扫描线从左到右或者从下到上扫描整个图形,在从左往右扫描的情况下,将每个矩形的始边和终边分别标记+1和-1,
线段树对于所有y离散化后的个数范围建树,其中维护两个信息,当前扫描线覆盖的线段的个数、当前扫描线覆盖的线段的长度
因为点是实数域的,所以接近无限,要对点离散化,用起点y1和终点(y2-1)来表示线段,线段树的结点标号就是离散后的下标。
有一个四元组\(( x_{1},y_{1},y_{2},+1)\)和\((x_{2},y_{1},y_{2},-1)\)分别标记矩形开始的边和结束的边
不需要延迟标记,当前扫描线覆盖的长度都维护在根节点上。
Code
#include <bits/stdc++.h>
#define pb push_back
#define db double
using namespace std;
const int N = 100010;
int n;
struct Segment
{
double x, y1, y2;
int k;
bool operator< (const Segment &t)const
{
return x < t.x;
}
}seg[N * 2];
struct Node
{
int l, r;
int cnt;
double len;
}tr[N * 8];
vector<double> ys;
int find(db y){
return lower_bound(ys.begin(),ys.end(),y)-ys.begin();
}
void push_up(int u)
{
if (tr[u].cnt) tr[u].len = ys[tr[u].r + 1] - ys[tr[u].l];//当前区间有覆盖,加上当前区间代表线段的长度
else if (tr[u].l != tr[u].r)
{
tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;//将当前的总长度传递到根节点
}
else tr[u].len = 0;
}
void build(int u, int l, int r)
{
tr[u] = {l, r, 0, 0};
if (l != r)
{
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
}
void change(int u,int l,int r,int k){
if(tr[u].l >= l && tr[u].r <= r){
tr[u].cnt += k;
push_up(u);
}
else {
int mid = tr[u].l + tr[u].r >> 1;
if( l <= mid ) change(u<<1,l,r,k);
if(r>mid) change(u<<1|1,l,r,k);
push_up(u);
}
}
int main(){
int T=1;
while(cin>>n,n){
ys.clear();
for(int i=0,j=0;i<n;i++){
double x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
ys.pb(y1);
ys.pb(y2);
seg[j++]={x1,y1,y2,1};
seg[j++]={x2,y1,y2,-1};
}
sort(ys.begin(),ys.end());
ys.erase(unique(ys.begin(),ys.end()),ys.end());
sort(seg,seg+2*n);
build(1,0,ys.size()-2);//线段个数比点的个数少1
db res=0;
for(int i=0;i<n*2;i++){
if(i > 0) res += tr[1].len*(seg[i].x-seg[i-1].x);
change(1,find(seg[i].y1),find(seg[i].y2)-1,seg[i].k);
}
printf("Test case #%d\n", T ++ );
printf("Total explored area: %.2lf\n\n", res);
}
}