题目大意
给你n个矩形,求这n个矩形的面积并
解题思路
扫描线算法
用一条竖直直线扫过整个坐标系,直线上被并集图形覆盖的长度只会在矩形的左右边界处发生变化
具体来说,我们将整个并集图形可以被分成
2
×
n
2\times n
2×n段,每一段之中直线覆盖的长度是固定的(记为L),所以这一段的面积为 L*这一段的宽度,各段面积之和即为所求,这条直线就是扫描线。
我们可以取出n个矩形的左右边界。若一个矩形的两个对角顶点坐标为
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1)和
(
x
2
,
y
2
)
(x_2,y_2)
(x2,y2),设
x
1
<
x
2
x_1<x_2
x1<x2,
y
1
<
y
2
y_1<y_2
y1<y2,则左边界记为四元组
(
x
1
,
y
1
,
y
2
,
1
)
(x_1, y_1, y_2, 1)
(x1,y1,y2,1),右边界记为
(
x
2
,
y
1
,
y
2
,
−
1
)
(x_2, y_1, y_2, -1)
(x2,y1,y2,−1),把这
2
n
2n
2n个四元组按照
x
x
x递增排序。
同时,我们把
y
y
y 值离散化一下,记
v
a
l
(
y
)
val(y)
val(y) 表示
y
y
y 被离散化之后的位置,
r
a
w
(
i
)
raw(i)
raw(i) 表示位置
i
i
i 对应的
y
y
y 的原始值,建立一个数组
c
c
c,用
c
[
i
]
c[i]
c[i] 表示扫描线上第
i
i
i 段被覆盖的次数,
c
c
c 数组初始化为0,用线段树来维护数组
c
c
c
逐一扫描
2
×
n
2\times n
2×n个四元组,设当前的四元组为
(
x
,
y
1
,
y
2
,
k
)
(x, y_1, y_2, k)
(x,y1,y2,k),那么我们就对线段树更新,
v
a
l
(
y
1
)
val(y_1)
val(y1) 到
v
a
l
(
y
2
)
−
1
val(y_2)-1
val(y2)−1 加上
k
k
k(为什么是
v
a
l
(
y
2
)
−
1
val(y_2)-1
val(y2)−1 ,因为
y
y
y 是孤立的点,我们要维护的是这些点之间的线段,线段数比点的个数少一个,注意一下对应关系),我们通过
k
k
k 来巧妙地表示矩形边界的进出,然后在线段树上同时统计这些线段的总长度。
如何用线段树统计这些
y
y
y 构成的线段的总长度呢,我们对于一个树上的结点记录一个
c
n
t
cnt
cnt 表示该结点的
c
c
c 数组的值,同时记录一个
d
a
t
dat
dat 值表示这个点覆盖的线段的总长度,如何更新
d
a
t
dat
dat 呢?当当前节点的
c
n
t
cnt
cnt 值为0,则
d
a
t
dat
dat 等于左右子树的
d
a
t
dat
dat 之和,当当前节点的
c
n
t
cnt
cnt 值不为0,则
d
a
t
dat
dat 等于节点覆盖区间的线段总长度。
代码
#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int MAXN = 2e5 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int n;
struct node{
double x, y1, y2;
int k;
bool operator<(node b) const {
return x < b.x;
}
};
double ls[MAXN];
double alls[MAXN];
int lscnt;
int ask(double x){
return lower_bound(ls+1, ls+1+lscnt, x) - ls;
}
struct node1{
int l, r;
double val;
int cnt;
}tree[MAXN << 2];
void pushup(int rt){
if(tree[rt].cnt)
tree[rt].val = ls[tree[rt].r+1] - ls[tree[rt].l];
else
tree[rt].val = tree[rt << 1].val + tree[rt << 1 | 1].val;
}
void build(int l, int r, int rt){
tree[rt].l = l;
tree[rt].r = r;
if(l == r){
tree[rt].val = tree[rt].cnt = 0;
return ;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m+1, r, rt << 1 | 1);
pushup(rt);
}
void update(int L, int R, int x, int rt){
if(L <= tree[rt].l && tree[rt].r <= R){
tree[rt].cnt += x;
pushup(rt);
return ;
}
int m = (tree[rt].l + tree[rt].r) >> 1;
if(L <= m)
update(L, R, x, rt << 1);
if(m < R)
update(L, R, x, rt << 1 | 1);
pushup(rt);
}
void solve(){
lscnt = 0;
vector<node> v;
int cnt;
cnt = 0;
for(int i = 1; i <= n; i++){
double x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
v.pb(node{x1, y1, y2, 1});
v.pb(node{x2, y1, y2, -1});
alls[++cnt] = y1;
alls[++cnt] = y2;
}
sort(alls+1, alls+1+cnt);
alls[0] = -1;
for(int i = 1; i <= cnt; i++)
if(alls[i] != alls[i-1])
ls[++lscnt] = alls[i];
sort(v.begin(), v.end());
build(1, lscnt-1, 1);
double ans = 0;
double pre = 0;
for(int i = 0; i < 2*n; i++){
if(i == 0){
pre = v[i].x;
update(ask(v[i].y1), ask(v[i].y2)-1, v[i].k, 1);
}
else{
ans += (v[i].x - pre) * tree[1].val;
pre = v[i].x;
update(ask(v[i].y1), ask(v[i].y2)-1, v[i].k, 1);
}
}
cout << ans << endl << endl;;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
qc;
int T;
// cin >> T;
T = 1;
while(T--){
int t = 0;
while(cin >> n && n){
cout << "Test case #" << ++t << "\nTotal explored area: " << fixed << setprecision(2);
solve();
}
}
return 0;
}