【POJ】1151 Atlantis(线段树+扫描线+离散化)

题目大意:给若干个矩形,每次给的是矩形对角两个点的坐标,求所有矩形并起来的面积。
线段树扫描线典型题:
线段树的叶子节点表示的是该端点到达下一个端点的线段的长度:x[r+1]-x[l];
假如表示的是一个区间,那么线段树的值便是这个区间线段的长度+到达下一个端点的线段的长度。
并且在一轮下来,搜索到的矩形便是所有长度相加乘上按矩形高度从小到大排列的一个高度数组的相邻高度相减
扫描的原则是,从最低的高度开始扫描,一条假想的平行与x的水平线,开始扫描到下一个高度为止,相同高度的也算,得到一个矩形的面积。
重复。
给每个矩形做上标记,这个地方体会了很久,说的话我只能尽量。
给每个矩形建立两个结构体变量。
结构体为:
Struct seg
{
Double l,r,h;
int s;
}
其中l,r,h分别对应左x坐标和右x坐标,h对应的是矩形的高。
s便是我们所做的标记。
在大神的博客上是这么描述s
1、
S=1表示矩形的下边,s=-1表示矩形的上边。
2、
s表示矩形的覆盖情况。
个人觉得kuangbin的比较好理解,便是第二种。
这边需要好好理解扫描线的扫描原则:

这边从1这条线开始往上扫描,直到遇到下一高度的平行线,即2,在开始扫描的时候边将左矩形记录覆盖,覆盖的意思表示该矩形的区间范围(即它的底)在之后的所有计算面积过程当中都可以算进去,我们之后会枚举高的范围,假如该矩形的s=1,即是属于覆盖范围的话,那么面积都要加上h(此时枚举的高度)乘上该矩形的底。
言归正传,这边下一高度是2,所以我们这时候得到的第一个小矩形的面积h1*AB;这时候sum[1]=AB;
下一个我们从下一个高度即2开始扫描,那么下一个高度即是3.,之间的高为h2,那么右矩形这时候也加入覆盖的范围
所以这时候得到的是两个小矩形的面积,
1、h2*CD
2、H2*AB
Sum[1]=AB+CD;
这边解释一下sum[1]的作用,sum[1]表示的是这时候整个平面当中被扫描覆盖的所有矩形的底的总和。
即位于高度h2范围内所有矩形的底总和,这就是s的强大之处啊。
第三步便是从3开始继续向上扫描,这时候要让左矩形退出覆盖了,因为再往上的高度,左矩形已经不能再提供什么面积。
这边突然想到一个说法。
其实扫描线操作的仅仅是让你完成这个矩形底的计算,以及是否加入和退出覆盖范围,这个便是在一般线段树当中的插入和删除线段了。
而对于高度的搜索枚举,只需要排序相减就可以了。
第三步使左矩形退出覆盖范围,这时候sum[1]=CD
这时候高度是h4,所以最后得到的小矩形面积便是h4*CD
不知道是不是被一个老男人的思想挖掘给弄晕了,好吧。
我承认我是不理解,随便写一写,最后发现,我有点理解了。
我去实现一下我的代码~~~

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 220
#define ls rt<<1
#define rs ls|1
#define m (l+r)>>1
double sum[MAX << 2];
double x[MAX];
int col[MAX << 2];
struct seg
{
    double l, r, h;
    int s;
}tg[MAX];

bool cmp(seg a, seg b)
{
    return a.h < b.h;
}
/*
这个是父节点与儿子节点的更新函数。
这边需要注意一个与常规不一样的地方。
由于这边表示的是一个范围。
假如采取原来的方式,对于数据0-3这个
可以计算出0-2,但是对于3的话就是0。
所以应该采取下列的方式就可以完美解决。
将传的时候少传一个,操作的是往前+1.
*/
void uprt(int l,int r,int rt)
{
    if (col[rt])
        sum[rt] = x[r+1] - x[l];
    else
    if (l == r) sum[rt] = 0;
    else
        sum[rt] = sum[rs] + sum[ls];
}
/*
这个线段树的更新主要在于如何增减覆盖的区间。
并且将其更新到1的位置
*/
int lower_bound(double x[], int k, double s)
{
    int l = 0, r = k;
    while (l <= r)
    {
        int mid = m;
        if (x[mid] == s)return mid;
        if (x[mid] < s)
            l = mid + 1;
        else
            r = mid - 1;
    }
    return -1;
}
void updata(int L, int R, int s,int l, int r, int rt)
{
    if (L <= l&&r <= R)
    {
        col[rt] += s;
        uprt(l, r, rt);
        return;
    }
    int mid = m;
    if (L <= mid)
        updata(L, R, s,l, mid, ls);
    if (mid < R)
        updata(L, R, s,mid + 1, r, rs);
    uprt(l, r, rt);
}

int main()
{
    int n;
    int icase = 1;
    while (cin >> n&&n)
    {
        double x1, x2, y1, y2;
        int cnt = 0;
        for (int i = 0; i < n; i++)
        {
            scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
            x[cnt] = x1;
            tg[cnt].l = x1;
            tg[cnt].r = x2;
            tg[cnt].h = y1;
            tg[cnt++].s = 1;
            x[cnt] = x2;
            tg[cnt].l = x1;
            tg[cnt].r = x2;
            tg[cnt].h = y2;
            tg[cnt++].s = -1;
        }
        sort(x, x + cnt);
        sort(tg, tg + cnt, cmp);
        int cnt2 = 1;
        for (int i = 1; i < cnt;i++)
        if (x[i] != x[i - 1])
            x[cnt2++] = x[i];
        cnt2--;
        double ans = 0;
        memset(col, 0, sizeof(col));
        memset(sum, 0, sizeof(sum));

        for (int i = 0; i < cnt-1; i++)
        {
            int l = lower_bound(x, cnt2, tg[i].l);
            int r = lower_bound(x, cnt2, tg[i].r) - 1;
            updata(l, r, tg[i].s, 0,cnt2, 1);
            ans += sum[1] * (tg[i + 1].h - tg[i].h);
        }
        printf("Test case #%d\nTotal explored area: ", icase++);
        printf("%.2lf\n\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值