POJ 1151 Atlantis 线段树求矩形面积并 方法详解

时间:2022-10-14 13:04:44

第一次做线段树扫描法的题,网搜各种讲解,发现大多数都讲得太过简洁,不是太容易理解。所以自己打算写一个详细的。看完必会o(∩_∩)o

顾名思义,扫描法就是用一根想象中的线扫过所有矩形,在写代码的过程中,这根线很重要。方向的话,可以左右扫,也可以上下扫。方法是一样的,这里我用的是由下向上的扫描法。

POJ 1151 Atlantis 线段树求矩形面积并 方法详解

如上图所示,坐标系内有两个矩形。位置分别由左下角和右上角顶点的坐标来给出。上下扫描法是对x轴建立线段树,矩形与y平行的两条边是没有用的,在这里直接去掉。如下图。

POJ 1151 Atlantis 线段树求矩形面积并 方法详解

现想象有一条线从最下面的边开始依次向上扫描。线段树用来维护当前覆盖在x轴上的线段的总长度,初始时总长度为0。用ret来保存矩形面积总和,初始时为0。

由下往上扫描,扫描到矩形的底边时将它插入线段树,扫描到矩形的顶边时将底边从线段树中删除。而在代码中实现的方法就是,每条边都有一个flag变量,底边为1,顶边为-1。

用cover数组(通过线段树维护)来表示某x轴坐标区间内是否有边覆盖,初始时全部为0。插入或删除操作直接让cover[] += flag。当cover[] > 0 时,该区间一定有边覆盖。

开始扫描到第一条线,将它压入线段树,此时覆盖在x轴上的线段的总长度L为10。计算一下它与下一条将被扫描到的边的距离S(即两条线段的纵坐标之差,该例子里此时为3)。

则 ret += L * S. (例子里增量为10*3=30)

结果如下图

POJ 1151 Atlantis 线段树求矩形面积并 方法详解  橙色区域表示已经计算出的面积。

扫描到第二条边,将它压入线段树,计算出此时覆盖在x轴上的边的总长度。

例子里此时L=15。与下一条将被扫描到的边的距离S=2。 ret += 30。 如下图所示。

POJ 1151 Atlantis 线段树求矩形面积并 方法详解绿色区域为第二次面积的增量。

接下来扫描到了下方矩形的顶边,从线段树中删除该矩形的底边,并计算接下来面积的增量。如下图。

POJ 1151 Atlantis 线段树求矩形面积并 方法详解 蓝色区域为面积的增量。

此时矩形覆盖的总面积已经计算完成。 可以看到,当共有n条底边和顶边时,只需要从下往上扫描n-1条边即可计算出总面积。

==============================      分割线      ========================================

此题因为横坐标包含浮点数,因此先离散化。另外,因为用线段树维护的是覆盖在x轴上的边,而边是连续的,并非是一个个断点,因此线段树的每一个叶子结点实际存储的是该点与下一点之间的距离。代码中r+1, r-1的地方多多体会。代码我是看着HH大神的代码写的,基本一样。在这里膜拜一下。。。

 #include<stdio.h>
#include<string.h>
#include<algorithm>
#define maxn 222
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
using namespace std;
int cover[maxn<<];
double sum[maxn<<], x[maxn];
struct seg
{
double l, r, h;
int flag;
seg() {}
seg(double a,double b,double c,int d) : l(a), r(b), h(c), flag(d) {}
bool operator < (const seg &cmp) const
{
return h < cmp.h;
}
}ss[maxn];
int bin(double key, int len, double x[])
{
int l = , r = len - ;
while (l <= r)
{
int m = (l + r) >> ;
if (key == x[m]) return m;
else if (key < x[m]) r = m - ;
else l = m + ;
}
return -;
}
void PushUp(int rt,int l,int r)
{
if (cover[rt]) sum[rt] = x[r+] - x[l];
else if (l == r) sum[rt] = ;
else sum[rt] = sum[rt<<] + sum[rt<<|];
}
void update(int L,int R,int f,int l,int r,int rt)
{
if (L <= l && r <= R)
{
cover[rt] += f;
PushUp(rt, l, r);
return;
}
int m = (l + r) >> ;
if (L <= m) update(L, R, f, lson);
if (m < R) update(L, R, f, rson);
PushUp(rt, l, r);
}
int main()
{
int n;
int cas = ;
//freopen("data.in","r",stdin);
while (~scanf("%d",&n) && n)
{
int m = ;
for (int i = ; i < n; i++)
{
double x1,y1,x2,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
x[m] = x1;
ss[m++] = seg(x1, x2, y1, );
x[m] = x2;
ss[m++] = seg(x1, x2, y2, -);
}
sort(x, x + m);
sort(ss, ss + m);
int k = ;
for (int i = ; i < m; i++)
if (x[i] != x[i-]) x[k++] = x[i];
memset(cover, , sizeof(cover));
memset(sum, , sizeof(sum));
double ret = ;
for (int i = ; i < m - ; i++)
{
int l = bin(ss[i].l, k, x);
int r = bin(ss[i].r, k, x) - ;
if (l <= r) update(l, r, ss[i].flag, , k - , );
ret += sum[] * (ss[i+].h - ss[i].h);
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas++,ret);
}
return ;
}