线段树专题-----P - Atlantis 扫描线+线段树

Atlantis
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 11551 Accepted Submission(s): 4906

Problem 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 file 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

初次学习扫描线,看了好几个博客加上代码理解才勉强看懂:
推荐博客:
介绍左闭右开区间怎么取:https://www.cnblogs.com/Blackops/p/6027209.html
辅助理解:http://www.cnblogs.com/scau20110726/archive/2013/04/12/3016765.html
代码理解:https://blog.csdn.net/qq_18661257/article/details/47622677
线段树+扫描线加深理解:https://blog.csdn.net/qq_18661257/article/details/47658191

主要思路:
由于横坐标是浮点值,所以必须离散化横坐标,按从小到大进行排序;
存储矩形的上下边,用1代表下边,用-1代表上边;按高度对边从小到大进行排序;
遍历存储的边(相当于用扫描线进行扫描),用下一条边的高度减去当前这条边的高度,乘上总区间被覆盖的长度,就能得到最终的面积;
sum数组存储该区间内横跨的横坐标的长度

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid + 1,r
const int MAXN = 2005;
int cover[MAXN << 2],n,cnt,res;
double X[MAXN << 2],sum[MAXN << 2];

struct seg{
    double l,r,h; // 记录矩形的上下边,左端点,右端点,高度
    int cnt;//下边为1,上边为-1
    seg(){}
    seg(double l,double r,double h,int cnt):l(l),r(r),h(h),cnt(cnt){}
    friend bool operator < (const seg &p,const seg &q){
        return p.h < q.h;
    }
}s[MAXN];

void pushup(int rt,int l,int r)
{
    //如果cover[rt] == 1,证明该区间被完全覆盖了,进行赋值;
    //如果cover[rt] == 0,该叶子节点一定为0,如果不是叶子节点,代表该区间没有被完全覆盖
    //根据子节点的覆盖区间大小,来得到当前的覆盖区间大小
    if(cover[rt]) sum[rt] = X[r + 1] - X[l];  //利用[ , ),这个区间性质,左闭右开
    else sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void update(int rt,int l,int r,int ql,int qr,int val)
{
    if(l >= ql && r <= qr){
        cover[rt] += val;
        pushup(rt,l,r);
        return;
    }
    int mid = (l + r) >> 1;
    if(ql <= mid) update(lson,ql,qr,val);
    if(qr > mid) update(rson,ql,qr,val);
    pushup(rt,l,r);
}

int binary(double x)
{
    int l = 0,r = res - 1;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(X[mid] == x) return mid;
        else if(X[mid] > x) r = mid - 1;
        else l = mid + 1;
    }
}

int main()
{
    int t = 0;
    int n;
    while(~scanf("%d",&n))
    {
        if(n == 0) break;
        cnt = res = 0;
        for(int i = 0;i < n;++i){
            double a,b,c,d;
            //输入矩形的左上角和右下角
            scanf("%lf %lf %lf %lf",&a,&b,&c,&d);
            s[cnt] = seg(a,c,b,1);
            X[cnt++] = a;
            s[cnt] = seg(a,c,d,-1);
            X[cnt++] = c;
        }
        sort(X,X + cnt);
        sort(s,s + cnt);
        res++;
        //去重
        for(int i = 1;i < cnt;++i){
            if(X[i] != X[i - 1]) X[res++] = X[i];
        }

        memset(sum,0,sizeof(sum));
        memset(cover,0,sizeof(cover));
        double ans = 0;
        for(int i = 0;i < cnt - 1;++i){
            //线段树中的区间[l,r],代表现实中的区间[l,r + 1)
            //所以pushup函数里面统计的时候,需要计算现实的数值
            int l = binary(s[i].l);
            int r = binary(s[i].r) - 1;
            update(1,0,res - 1,l,r,s[i].cnt);
            ans += sum[1] * (s[i + 1].h - s[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2lf\n\n",++t , ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值