zoukankan      html  css  js  c++  java
  • AcWing 247. 亚特兰蒂斯 | 扫描线

    传送门

    题目描述

    有几个古希腊书籍中包含了对传说中的亚特兰蒂斯岛的描述。

    其中一些甚至包括岛屿部分地图。

    但不幸的是,这些地图描述了亚特兰蒂斯的不同区域。

    您的朋友Bill必须知道地图的总面积。

    你自告奋勇写了一个计算这个总面积的程序。

    输入格式

    输入包含多组测试用例。

    对于每组测试用例,第一行包含整数n,表示总的地图数量。

    接下来n行,描绘了每张地图,每行包含四个数字x1,y1,x2,y2x1,y1,x2,y2(不一定是整数),(x1,y1)(x1,y1)和(x2,y2)(x2,y2)分别是地图的左上角位置和右下角位置。

    当输入用例n=0时,表示输入终止,该用例无需处理。

    输出格式

    每组测试用例输出两行。

    第一行输出”Test case #k”,其中k是测试用例的编号,从1开始。

    第二行输出“Total explored area:a”,其中a是总地图面积(即此测试用例中所有矩形的面积并,注意如果一片区域被多个地图包含,则在计算总面积时只计算一次),精确到小数点后两位数。

    在每个测试用例后输出一个空行。

    数据范围

    1≤n≤100,
    0≤x1<x2≤100000,
    0≤y1<y2≤100000

    输入样例:

    2
    10 10 20 20
    15 15 25 25.5
    0
    

    输出样例:

    Test case #1
    Total explored area: 180.00

    题意:求面积并

    题解:扫描线。

       如果题目给出的矩形如下:

       

        

        那么我们要求的总面积为:

        

       我们如果用一条竖直的直线从左到右扫过整个坐标系,那么直线上在所求面积中的覆盖长度L只会在每个矩形的左右边界发生变化,整个图形可以被分成2*n段,每一段直线被覆盖的长度L都是固定的,这一段的面积为L*∆x。

        

        我们知道∆x是两个相邻的x之间的间距,那我们要怎么维护每一段L的长度?

        对于每个矩形,我们把左边界记为一个四元组:(x1,y1,y2,1),把右边界记为(x2,y1,y2,-1)(假设x1<x2,y1<y2)。我们可以按纵坐标维护一个线段树,当直线扫过矩形的左边界时,相当于在线段树中把区间[y1,y2](y1<y2)的值都加上1(插入区间[y1,y2]),当直线扫过矩形的右边界时,相当于在线段树中把区间[y1,y2]的值都减去1(减去区间[y1,y2]),只要有位置值大于0就代表这是线段的一部分(算区间长度)。因为y的范围很大所以我们要先离散化一下。我们在线段树中定义cnt来记录这段区间出现了多少次,定义len来记录直线长度。每次将直线更新之后,根结点中存的长度就是当前段L的大小。

            

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int N = 200 + 10;
    struct node{
        double x,y1,y2;
        int val;
        node() {}
        node(double x, double y1, double y2,int val){
            this->x = x; this->val = val;
            this->y1 = y1; this->y2 = y2;
        }
        bool operator <(const node &t)const {
            return x<t.x;
        }
    };
    struct Tree{
        int l,r,cnt;
        double len;
    }t[1000010]; //找了一个小时的段错误,结果是*4小了???
    int n,k=1;
    vector<node> a;
    vector<double> y;
    void up(int rt) {
        if (t[rt].cnt>0) t[rt].len = y[t[rt].r+1] - y[t[rt].l];
        else t[rt].len = t[rt<<1].len + t[rt<<1|1].len;
    }
    void build(int rt,int l,int r) {
        t[rt].l = l,t[rt].r = r; t[rt].cnt = 0;
        t[rt].len = 0;
        if (l == r) return;
        int mid = (l+r)>>1;
        build(rt<<1,l,mid);
        build(rt<<1|1,mid+1,r);
    }
    void update(int rt,int l,int r,int val) {
        if (l <= t[rt].l && r >= t[rt].r) {
            t[rt].cnt+=val;
            up(rt);
            return;
        }
        int mid = (t[rt].l+t[rt].r)>>1;
        if (l <= mid) update(rt<<1,l,r,val);
        if (r > mid) update(rt<<1|1,l,r,val);
        up(rt);
    }
    int main() {
        while (~scanf("%d",&n) && n) {
            y.clear();a.clear();
            for(int i = 0; i < n; i++) {
                double x1,x2,y1,y2;
                scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
                y.push_back(y1);
                y.push_back(y2);
                a.push_back(node(x1,y2,y1,1));
                a.push_back(node(x2,y2,y1,-1));
            }
            y.push_back(-1);
            sort(a.begin(),a.end());
            sort(y.begin(),y.end());
            y.erase(unique(y.begin(),y.end()),y.end());
            int m = y.size(),num = a.size();
            build(1,0,m);
            double ans = 0;
            for (int i = 0; i < num; i++) {
                int l = lower_bound(y.begin(),y.end(),a[i].y2)-y.begin();
                int r = lower_bound(y.begin(),y.end(),a[i].y1) - y.begin()-1;
                update(1,l,r,a[i].val);
                ans += t[1].len*(a[i+1].x-a[i].x);
            }
            printf("Test case #%d
    ",k++);
            printf("Total explored area: %.2f
    
    ",ans);
        }
        return 0;
    }
    View Code

      

    PS:注意数组开大一点   

  • 相关阅读:
    C# 系统应用之通过注册表获取USB使用记录(一)
    web项目测试方法总结
    C#面向对象编程实例-猜拳游戏
    c#基础这些你都看过吗?(一)-----仅供初学者使用
    .NET事件监听机制的局限与扩展
    SQL代码
    泛型接口委托
    存储过程
    小操作
    DataGridView
  • 原文地址:https://www.cnblogs.com/l999q/p/11366840.html
Copyright © 2011-2022 走看看