矩形面积并 POJ Atlantis

 HDU 1542 PKU 1151 Atlantis 【线段树求矩形并】

 

Atlantis
Time Limit: 1000MS
Memory Limit: 10000K
Total Submissions: 9204
Accepted: 3617

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 

【题意】:

线段树经典应用,求矩形并的面积

【分析】:

第一道矩形几何题,主要是练习线段树来的

 

求矩形的并,由于矩形的位置可以多变,因此矩形的面积一下子不好求

这个时候,可以采用分割的思想,即把整块的矩形面积分割成几个小矩形的面积,然后求和就行了

这里我们可以这样做,把每个矩形投影到 y 坐标轴上来

然后我们可以枚举矩形的 x 坐标,然后检测当前相邻 x 坐标上 y 方向的合法长度,两种相乘就是面积

 

然后关键就是如何用线段树来维护那个 合法长度

可以这样来搞

线段树的节点这样定义

struct node { int left,right,cov; double len; }

cov 表示当前节点区间是否被覆盖,len 是当前区间的合法长度

 

然后我们通过扫描线的方法来进行扫描

枚举 x 的竖边,矩形的左边那条竖边就是入边,右边那条就是出边了

然后把所有这些竖边按照 x 坐标递增排序,每次进行插入操作

由于坐标不一定为整数,因此需要进行离散化处理

每次插入时如果当前区间被完全覆盖,那么就要对 cov 域进行更新

入边 +1 出边 -1

更新完毕后判断当前节点的 cov 域是否大于 0

如果大于 0,那么当前节点的 len 域就是节点所覆盖的区间

否则

如果是叶子节点,则 len=0

如果内部节点,则 len=左右儿子的 len 之和

----------------以上转自http://hi.baidu.com/legend_ni/blog/item/106e9f8a34ff9b04b21bba71.html

 

Problem: 1151


User: gdut_irelandken

Memory: 228K


Time: 0MS

Language: C++


Result: Accepted

Accepted

1542

0MS

268K

3041 B

C++

gdut_irelandken











 

#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
 
const int maxn = 201;  //离散化后,最多200个不同的值
 
double bin[maxn];  //离散化后存有有序且唯一的关键字的数组
 
int N;
 
//seg tree
struct seg
{
         int a,b;
         double len;
         int cov;
};
seg tree[maxn*4];
 
void maketree(int a,int b,int k)
{
         tree[k].a = a;
         tree[k].b = b;
         tree[k].len = 0;
         tree[k].cov = 0;
 
         if(a+1 == b)
                   return;
 
         int mid = (a+b)>>1;
         maketree(a,mid,k<<1);
         maketree(mid,b,k<<1|1);
}
 
inline void update_info(int k)
{
         if(tree[k].cov == 0)
                   tree[k].len = tree[k<<1].len + tree[k<<1|1].len;
}
void update(int c,int d,char flag,int k)
{
         if(c <= tree[k].a && tree[k].b <= d)
         {
                   if(flag == 1) //insert
                   {
                            tree[k].cov++;
                            tree[k].len = bin[tree[k].b] - bin[tree[k].a];
                   }
                   else //del
                   {
                            tree[k].cov --;
                            if(tree[k].cov == 0)
                            {
                                     if(tree[k].a +1 == tree[k].b) //叶结点
                                               tree[k].len = 0;
                                     else
                                               tree[k].len = tree[k<<1].len + tree[k<<1|1].len;
                            }
                   }
                   return;
         }
 
         if(c < tree[k<<1].b)
                   update(c,d,flag,k<<1);
 
         if(d > tree[k<<1|1].a)
                   update(c,d,flag,k<<1|1);
 
         update_info(k);
}
 
 
//垂直线
struct VLine
{
         double x;
         int y1,y2;
         char flag; // 1:入边 -1: 出边
};
 
bool cmp(VLine vl1,VLine vl2)
{
         return vl1.x < vl2.x;
}
 
VLine VL[maxn];
int tail;
 
double X1[maxn],X2[maxn],Y1[maxn],Y2[maxn];
 
void Add(double x1,int y1,double x2,int y2)
{
         if(x1 == x2 || y1 == y2)   //判断是不是一条线或点
                   return;
        
         VL[tail].x = x1;
         VL[tail].y1 = y1;
         VL[tail].y2 = y2;
         VL[tail].flag = 1;
         tail++;
 
         VL[tail].x = x2;
         VL[tail].y1 = y1;
         VL[tail].y2 = y2;
         VL[tail].flag = -1;
         tail++;
 
}
///
 
int size;
 
double tmp[maxn];
void decritize()
{
         for(int i=0; i<N; i++)
         {
                   tmp[i*2] = Y1[i];
                   tmp[i*2+1] = Y2[i];
         }
        
         sort(tmp,tmp+2*N);
 
         size = 1;
         bin[1] = tmp[0];
 
         for(int i=1; i<2*N; i++)   //离散化为1..size
         {
                   if(tmp[i] != bin[size]) //  要保证bin里的是互不相同的
                            bin[++size] = tmp[i];
         }
}
 
inline int Binary(double a[],int l,int r,double x) //二分查找x,并返回为序号   O(logN)
{
         int mid;
         while(l<=r)          //这里少了个=,害自己调试半天..要记住..
         {
                   mid = (l+r)>>1;
 
                   if(x < a[mid])
                            r = mid -1;
                   else
                   if(x > a[mid])
                            l = mid +1;
                   else
                            return mid;
         }
}
 
void input()
{
         tail = 0;
 
         for(int i = 0; i<N; i++)
                   scanf("%lf%lf%lf%lf",&X1[i],&Y1[i],&X2[i],&Y2[i]);
        
         decritize();
 
         for(int i=0; i<N; i++)
 
                   Add( X1[i],    Binary(bin,1,size,Y1[i]),   X2[i],   Binary(bin,1,size,Y2[i])  );   //注意加入的矩形是Y轴离散化后的矩形
}
 
int main()
{
         int T = 0;
 
         while(scanf("%d",&N) && N)
         {
                   input();
 
                   maketree(1,size,1);
 
                   sort(VL,VL+tail,cmp);
 
                   double area = 0;
 
                   update(VL[0].y1,VL[0].y2,VL[0].flag ,1);
 
                   for(int i=1; i<tail; i++)       //扫描线
                   {
                            area += (VL[i].x - VL[i-1].x) * tree[1].len;
 
                            update(VL[i].y1,VL[i].y2,VL[i].flag ,1);
                   }
                   printf("Test case #%dnTotal explored area: %.2lfnn",++T,area); 
         }
         return 0;
}
 


这里update一下自己的代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define maxn 210
using namespace std;
const double eps = 1e-5;
int n;

struct opt{
    double x, a, b;
    int tp;
    int l, r;
    opt(double x_ = 0, double a_ = 0, double b_ = 0, int tp_ = 0, int l_ = 0, int r_ = 0){
        x = x_;
        a = a_;
        b = b_;
        tp = tp_;
        l = l_;
        r = r_;
    }
}q[maxn << 2];

bool cmp(const opt& a, const opt& b){
    return a.x < b.x;
}
int amt;

double h[maxn << 2];
int cnt;

struct Node{
    int vis;
    double len, cov;
    void clear(){vis = len = cov = 0;}
}t[maxn << 2];
#define lc id << 1
#define rc id << 1 | 1

void pushup(int id){if(t[id].vis == 0)t[id].cov = t[lc].cov + t[rc].cov;}

void build(int id, int l, int r){
    t[id].clear();
    if(l == r){
        if(l != 1)t[id].len = h[l] - h[l-1];
        else t[id].len = 0;
        return;
    }
    int mid = l + r >> 1;
    build(lc, l, mid);
    build(rc, mid+1, r);
    t[id].len = t[lc].len + t[rc].len;
}

void update(int id, int l, int r, int L, int R, int val){
    if(L == l && R == r){
        if(val == 1){
            t[id].vis ++;
            t[id].cov = t[id].len;
        }
        else{
            t[id].vis --;
            if(t[id].vis == 0){
                if(l == r)t[id].cov = 0;
                else pushup(id);
            }
        }
        return;
    }
    int mid = l + r >> 1;
    if(R <= mid)update(lc, l, mid, L, R, val);
    else if(L > mid)update(rc, mid+1, r, L, R, val);
    else update(lc, l, mid, L, mid, val), update(rc, mid+1, r, mid+1, R, val);
    pushup(id);
}

int main(){
    int Case = 0;
    while(scanf("%d", &n) && n){
        double a, b, c, d;
        cnt = amt = 0;
        for(int i = 1; i <= n; i ++){
            scanf("%lf%lf%lf%lf", &a, &b, &c, &d);
            h[++ cnt] = b, h[++ cnt] = d;
            q[++ amt] = opt(a, b, d, 1);
            q[++ amt] = opt(c, b, d, -1);
        }
        sort(h + 1, h + 1 + cnt);
        cnt = unique(h + 1, h + 1 + cnt) - h - 1;
        for(int i = 1; i <= amt; i ++){
            q[i].l = lower_bound(h + 1, h + 1 + cnt, q[i].a) - h;
            q[i].r = lower_bound(h + 1, h + 1 + cnt, q[i].b) - h;
        }
        sort(q + 1, q + 1 + amt, cmp);
        build(1, 1, cnt);
        double ans = 0;
        for(int i = 1; i <= amt; i ++){
            ans += t[1].cov * (q[i].x - q[i-1].x);
            update(1, 1, cnt, q[i].l+1, q[i].r, q[i].tp);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n", ++ Case, ans);
        puts("");
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值