Problem
Reference
扫描线算法完全解析
矩形面积并、矩形面积交、矩形周长并(线段树、扫描线总结)
POJ1151(线段树+扫描线求矩形面积并)
算法总结:【线段树+扫描线】&矩形覆盖求面积/周长问题(HDU 1542/HDU 1828)
Meaning
平面上有若干个矩形,可能有重叠,问总的面积是多少(重叠部分只算一次)
Analysis
这是扫描线求矩形面积并的裸题。
总的思路就是把几个重叠在一起的矩形所组成的多边形,切成一个个小矩形,分别统计答案。
以从下往上扫描为例(上图来自:poj 1151 Atlantis】线段树之扫描线(面积并))。关注矩形的上、下边,这些边(看作分界线)把平面分成若干个区间,每个区间都报包含一个切出来的小矩形。这个矩形的高,就是相邻两条分界线之间的距离,而底边长就是在当前扫描线(即上图红线)上的那些边并起来的长度(即处于红线上的矩形边的并),底乘高就是这块被切出来的小矩形的面积。
怎么统计扫描线上的总长度呢?用线段树。
在从下往上扫的过程中(扫描,其实就先是把所有矩形上、下边放到一起,按 y 轴坐标从低到高拍序,然后从低到高地遍历这些边,也即遍厉分界线),每遇到一条边,看它是上边还是下边。如果是下边,就把这条边“并”进总长里;上边则删掉这部分,即抵消到当初上边并进来的那部分。
这份代码里,线段树的叶子对应的是“点”,而不是“区间”,至少两个点才能夹出一个区间,所以当区间长度只有 2 的时候,不应再往下搜树。
也可以改写成叶子对应一个区间的写法。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1000;
struct seg
{
int l, r; // 边的左、右边界坐标
int h; // 边的高度
int v; // 标记上下边:1 -> 下边,-1 -> 上边
seg(int _l = 0, int _r = 0, int _h = 0, int _v = 0):
l(_l), r(_r), h(_h), v(_v) {}
bool operator < (const seg &rhs) const
{
return h < rhs.h;
}
} s[N<<1];
int x[N<<1], cnt[N<<3], len[N<<3];
void pushup(int l, int r, int rt)
{
if(cnt[rt])
len[rt] = x[r] - x[l];
else if(l + 1 >= r) // 已是最小区间,没有子区间
len[rt] = 0;
else
len[rt] = len[rt<<1] + len[rt<<1|1];
}
void update(int ul, int ur, int v, int l, int r, int rt)
{
if(ul <= l && r <= ur)
{
cnt[rt] += v;
pushup(l, r, rt);
return;
}
int m = l + r >> 1;
if(ul < m) // 等于的时候也是不需要的,因为一个点不是区间
update(ul, ur, v, l, m, rt<<1);
if(ur > m) // 同样不取等号
update(ul, ur, v, m, r, rt<<1|1); // [m,r]!不是[m+1,r]!
pushup(l, r, rt);
}
int main()
{
int n;
while(~scanf("%d", &n) && n)
{
for(int i = 0, xl, xr, yl, yr; i < n; ++i)
{
scanf("%d%d%d%d", &xl, &yl, &xr, &yr);
// 下边是 1,表示并入
s[i] = seg(xl, xr, yl, 1);
// 下边-1,表示删除
s[i+n] = seg(xl, xr, yr, -1);
x[i] = xl;
x[i+n] = xr;
}
n <<= 1;
// 边从低到高排
sort(s, s + n);
sort(x, x + n);
memset(len, 0, sizeof len);
memset(cnt, 0, sizeof cnt);
int m = unique(x, x + n) - x, ans = 0;
for(int i = 0, l, r; i < n - 1; ++i)
{
// 离散化横坐标
l = lower_bound(x, x + m, s[i].l) - x;
r = lower_bound(x, x + m, s[i].r) - x;
// 并入或删除边
update(l, r, s[i].v, 0, m, 1);
// 加入小矩形面积
ans += len[1] * (s[i+1].h - s[i].h);
}
printf("%d\n", ans);
}
puts("*");
return 0;
}