zoukankan      html  css  js  c++  java
  • 【42.49%】【hdu 1542】Atlantis(线段树扫描线简析)

    Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
    Total Submission(s): 11558 Accepted Submission(s): 4910

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

    Source
    Mid-Central European Regional Contest 2000

    【题解】

    给定几个矩形的左下角和右上角;求所有矩形的面积(重复部分不能多算);
    线段树,扫描线;
    扫描线的思路就是假想一条线段从图的最下面往上扫描;
    我们计算的是扫描线扫过的所有矩形区域的面积;
    盗下图
    这里写图片描述
    需要把横坐标离散化下;
    然后处理出每个矩形的两条横边(上边和下边,上边记为-1,下边记为1);
    然后记录所有横边的纵坐标;
    然后以所有横边的纵坐标作为关键字升序排下;
    从纵坐标小的开始处理;
    处理到①的时候;看下②的高度是多少
    然后看一下①->②这个线段范围内;横坐标有哪些区域被矩形占据了;
    求出这个占据的长度sum[1](即线段树的第一个节点
    然后答案递增sum[1]*(h②-h①);
    处理下一条线段;
    如果下一条线段是上边。就把它相应的下边对应的横坐标范围减去;下次就不会再处理了;
    嘛,多看下代码结合图理解下吧。不会很难
    代码中cnt是这个区间内下边比上边多多少条边;
    显然如果cnt[rt]==0则表示这个区间内没有需要处理的矩形覆盖的范围;
    大于0则表示有一个矩形覆盖到了这个区间。且还没有处理完;
    (处理到上边的时候会减去相应下边这段的cnt值);
    sum[rt]则表示这个区间内被矩形覆盖的线段长度;

    /*
    看完代码之后你们可能会对那个r+1和r-1有点疑问;
    这样先对询问把r减去1,是为了防止线段树在划分的时候
    出现
    1-6
    被分成
    1-5和6..6的情况;
    右边那个区间只有1个点;而我们没办法用x[6]-x[6]来表示这段长度;
    而左边是a[5]-a[1];显然会少掉一段5..6没被加进去;
    因此我们先减去1;
    这样就只会分成
    1..5这一段;
    然后加的时候改为a[6]-a[1]即可;
    即a[r+1]-a[l];
    */

    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    #include <set>
    #include <map>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    #include <vector>
    #include <stack>
    #include <string>
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define LL long long
    
    using namespace std;
    
    const int MAXN = 200+10;
    const int dx[5] = {0,1,-1,0,0};
    const int dy[5] = {0,0,0,-1,1};
    const double pi = acos(-1.0);
    
    struct hx
    {
        double l,r,h,k;
    };
    
    int n,num = 0,cnt[MAXN<<2];
    double sum[MAXN<<2];
    vector <double> a;
    hx bian[MAXN];
    
    void input_LL(LL &r)
    {
        r = 0;
        char t = getchar();
        while (!isdigit(t) && t!='-') t = getchar();
        LL sign = 1;
        if (t == '-')sign = -1;
        while (!isdigit(t)) t = getchar();
        while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
        r = r*sign;
    }
    
    void input_int(int &r)
    {
        r = 0;
        char t = getchar();
        while (!isdigit(t)&&t!='-') t = getchar();
        int sign = 1;
        if (t == '-')sign = -1;
        while (!isdigit(t)) t = getchar();
        while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
        r = r*sign;
    }
    
    bool cmp(hx a,hx b)
    {
        return a.h<b.h;
    }
    
    void up_data(int L,int R,int c,int l,int r,int rt)
    {
        if (L<=l && r<=R)
        {
            cnt[rt]+=c;
            if (cnt[rt])
                sum[rt] = a[r+1]-a[l];
            else//变成0了也不能认定内部就没有需要处理的区间了;还要加上内部的;
                if (l==r)
                    sum[rt]=0;
                else
                    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
            return;
        }
        int m = (l+r)>>1;
        if (L <= m)
            up_data(L,R,c,lson);
        if (m < R)
            up_data(L,R,c,rson);
        if (cnt[rt])//有可能内部下边已经处理完了,但是外部还有下边,所以不能直接写成sum[rt]=sum[rt<<1]+sum[rt<<1|1]
                sum[rt] = a[r+1]-a[l];
        else
            if (l==r)
                sum[rt] == 0;
            else
                sum[rt] = sum[rt<<1]+sum[rt<<1|1];
    }
    
    int main()
    {
        //freopen("F:\rush.txt","r",stdin);
        input_int(n);
        int tt = 0;
        while (n!=0)
        {
            memset(cnt,0,sizeof(cnt));
            memset(sum,0,sizeof(sum));
            a.clear();num = 0;
            for (int i = 1;i <= n;i++)
            {
                double x1,y1,x2,y2;
                scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
                a.push_back(x1);
                a.push_back(x2);
                num++;
                bian[num].l = x1,bian[num].r = x2,bian[num].h = y1,bian[num].k = 1;//横线的下边
                num++;
                bian[num].l = x1,bian[num].r = x2,bian[num].h = y2,bian[num].k = -1;//记录横线的上边
            }
            sort(a.begin(),a.end());
            a.erase(unique(a.begin(),a.end()),a.end());//x轴离散化
            sort(bian+1,bian+1+num,cmp);
            double ans = 0;
            for (int i = 1;i <= num-1;i++)
            {
                int l = lower_bound(a.begin(),a.end(),bian[i].l)-a.begin();
                int r = lower_bound(a.begin(),a.end(),bian[i].r)-a.begin()-1;//先把右端点-1
                up_data(l,r,bian[i].k,0,a.size()-1,1);
                ans+=sum[1]*(bian[i+1].h-bian[i].h);
            }
            printf("Test case #%d
    Total explored area: %.2lf
    
    ",++tt,ans);
            input_int(n);
        }
        return 0;
    }
    
  • 相关阅读:
    网络中有三种通讯模式:单播、广播、组播(多播)
    chmod命令
    linux bash 命令重定向和多命令执行
    linux中管道符“|”的作用
    web测试常用的 linux 命令
    集群主要分成三大类 (高可用集群, 负载均衡集群,科学计算集群)
    iOS 监测电话呼入
    iOS NSUserDefaults [setValue:forKey:] [setObject:forKey:] <Objc> setValue(_,forKey:) set(_,forKey) <Swift 3>
    iOS 将navigationItem.titleView设置为自定义UISearchBar (Ficow实例讲解)
    iOS 加载Viewcontroller的几种方法
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632089.html
Copyright © 2011-2022 走看看