POJ 1151 Atlantis 线段树面积并

本文介绍了一种计算多个地图区域合并后的总面积的方法。通过扫描线算法,将不同地图的矩形区域进行合并,解决了计算合并面积的问题。文章详细阐述了算法的具体实现步骤,包括离散化坐标、区间更新等关键环节。

Atlantis
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 19878 Accepted: 7523
Description

There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the total area for which maps exist. You (unwisely) volunteered to write a program that calculates this quantity.
Input

The input consists of several test cases. Each test case starts with a line containing a single integer n (1 <= n <= 100) of available maps. The n following lines describe one map each. Each of these lines contains four numbers x1;y1;x2;y2 (0 <= x1 < x2 <= 100000;0 <= y1 < y2 <= 100000), not necessarily integers. The values (x1; y1) and (x2;y2) are the coordinates of the top-left resp. bottom-right corner of the mapped area.
The input file is terminated by a line containing a single 0. Don’t process it.
Output

For each test case, your program should output one section. The first line of each section must be “Test case #k”, where k is the number of the test case (starting with 1). The second one must be “Total explored area: a”, where a is the total explored area (i.e. the area of the union of all rectangles in this test case), printed exact to two digits to the right of the decimal point.
Output a blank line after each test case.
Sample Input

2
10 10 20 20
15 15 25 25.5
0
Sample Output

Test case #1
Total explored area: 180.00

题意,给n个矩形的左上角坐标和右下角坐标,求出这些矩形面积合并起来总共的面积。

画出图,从最底下一根线网上扫描,计算下底边合并起来的总长度乘以这一横边到上一横边之间的高,扫描到最定边的下面一条边就行了。

坐标的记录,因为都是浮点数,所以进行离散化,过程就是用一个数组记录下来,排序,去重,需要时二分查找点就行了。

对len[]进行更新,len[id]对应区间[l,r],表示在[l,r]区间内,有多少线段是被覆盖了,这些被覆盖的区间的长度用来更新到根节点len[1],len[1]表示在总区间内多少线段被覆盖,然后用于乘高度算面积。

我用x[]来离散化点,这里的x[]中的每个点应该理解为这个点对应到下一个点的总长度,对len[]更新的是更新的区间被覆盖长度,所以进行更新的时候r要减1。想成是长度,再去想更新,更容易理解。

既然成段更新,为什么不Push_Down()?因为下边与上边总是成对的加减,标记会自动变化,遇到上边标记-1即抵消下边的值1,因为各边是叠加状态,只要不为0,都会算在面积并里面。

#include"stdio.h"
#include"iostream"
#include"algorithm"
#include"string.h"
#include"queue"
#include"math.h"
typedef long long LL;
const int maxn = 2000+10;
using namespace std;

struct node
{
    double l,r,h;     ///矩形横边左右坐标,上边或下边高度h
    int flag;        ///标记上下边,上边下边任意边为-1都不影响
}poi[maxn*4];
double len[maxn*4];///区间长度
double x[maxn*4]; ///离散化用
int mark[maxn*4];///标记

void add(double x1,double x2,double h,double flag,int cnt) ///存入边
{
    poi[cnt].l = x1;
    poi[cnt].r = x2;
    poi[cnt].h = h;
    poi[cnt].flag = flag;
}
bool cmp(node a,node b)
{
    return a.h < b.h;
}

int Find(double val,int lon)  ///二分查找
{
    int l = 0;
    int r = lon;
    while(l <= r)
    {
        int mid = (l+r)/2;
        if(x[mid] == val)
            return mid;
        else if(val < x[mid])
            r = mid-1;
        else
            l = mid+1;
    }
}

void Push_Up(int l,int r,int id)         ///向上更新
{
    if(mark[id]) len[id] = x[r+1]-x[l];  ///有标记就计算长度
    else if(l == r) len[id] = 0;         ///对于结点,标记为0,不能进行覆盖,不算在面积长度里面
    else len[id] = len[id*2]+len[id*2+1];///对于其他区间,子节点标记不一定为0,进行更新操作,手动模拟可懂
}
void update(int L,int R,int c,int l,int r,int id)
{
    if(L <= l && r <= R)
    {
        mark[id] += c;
        Push_Up(l,r,id);  ///不确定是否被标记,是否是结点,所以更新
        return;
    }
    int mid = (l+r)/2;
    if(L <= mid) update(L,R,c,l,mid,id*2);
    if(mid < R)  update(L,R,c,mid+1,r,id*2+1);
    Push_Up(l,r,id);
}
int main(void)
{
    int n,cas = 1;
    while(scanf("%d",&n) && n)
    {
        int cnt = 0;
        for(int i = 1;i <= n;i++)
        {
            double x1,y1,x2,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            x[cnt] = x1;
            add(x1,x2,y1,-1,cnt);
            cnt++;
            x[cnt] = x2;
            add(x1,x2,y2,1,cnt);
            cnt++;
        }
        sort(poi,poi+cnt,cmp);
        sort(x,x+cnt);
        int lon = 1;
        for(int i = 1;i < cnt;i++)    ///离散化
        {
            if(x[i] != x[i-1])
                x[lon++] = x[i];
        }
        memset(len,0,sizeof len);
        memset(mark,0,sizeof mark);
        double ret = 0;
        for(int i = 0;i < cnt-1;i++)
        {

            int L = Find(poi[i].l,lon);
            int R = Find(poi[i].r,lon) - 1;   ///每个点对应一个线段,所以-1
            update(L,R,poi[i].flag,0,lon-1,1);
            ret += len[1]*(poi[i+1].h-poi[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n",cas++,ret);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值