hdu 1255 覆盖的面积(线段树 面积交+离散化)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1255

Description

给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.


 

Input

输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N&lt;=1000),代表矩形的数量,然后是N行数据,每一行包含四个浮点数,代表平面上的一个矩形的左上角坐标和右下角坐标,矩形的上下边和X轴平行,左右边和Y轴平行.坐标的范围从0到100000.

注意:本题的输入数据较多,推荐使用scanf读入数据.
 

Output

对于每组测试数据,请计算出被这些矩形覆盖过至少两次的区域的面积.结果保留两位小数.
 

Sample Input

    
    
2 5 1 1 4 2 1 3 3 7 2 1.5 5 4.5 3.5 1.25 7.5 4 6 3 10 7 3 0 0 1 1 1 0 2 1 2 0 3 1
 

Sample Output

    
    
7.63 0.00
 

注意对比面积并  http://blog.csdn.net/u014665013/article/details/49815307
点更新:

#include <iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int maxn=1100;
struct LINE
{
    double  x, y_down, y_up;
    int  flag;
    bool operator<(const LINE &a)const  ///按照x从小到大的顺序排序
    {
        return  x<a.x;
    }
}line[2*maxn];

struct TREE
{
    double  x,y_down, y_up;
    int     cover;  ///用以表示加进线段树中的线段次数
    bool    flag;   ///标记叶子节点
}tree[maxn*8];

double  y[2*maxn];

void build(int i, int l, int r) ///当前节点下标,l , r 线段树建立左右线数组下标
{
       tree[i].x = -1; //-1表示该区间已经没有线段
       tree[i].cover = 0; //表示该区间上有多少条线段;左边线段加进去则++,右边线段加进去则--
       tree[i].y_down = y[l];
       tree[i].y_up = y[r];
       tree[i].flag = false;
       if(l+1==r)
       {
           tree[i].flag = true; //flag==true表示达到了叶子节点
           return;
       }
       int mid=(l+r)>>1;
       build(2*i, l, mid);
       build(2*i+1, mid, r);
}

double insert(int i, double x, double l, double r, int flag) //flag表示为左边还是右边
{
    if ( r<=tree[i].y_down || l>=tree[i].y_up )   return 0;
    if (tree[i].flag) /// 叶子节点
    {
        if (tree[i].cover > 1) /// 该区域的面积存在,且未经计算
        {
             double temp_x = tree[i].x;
             double ans=( x-temp_x )*(tree[i].y_up - tree[i].y_down);
             tree[i].cover += flag;
             tree[i].x = x;   //定位上一次的x
             return ans;
        }
        else  ///虽然是叶子节点,但是需要更新当前的线段覆盖标记
        {
            tree[i].cover += flag;
            tree[i].x = x;  ///更新最新x
            return 0;
        }
    }
    return insert(2*i, x, l, r, flag)+insert(2*i+1, x, l, r, flag); ///不是叶子节点就往下递归
}

int main( )
{
   // freopen("d:\\in.txt","r",stdin);
    int  n,index;
    double  x1, y1, x2, y2;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n) ;
        index = 1;
        for (int i=1; i<=n; i++)
        {
            scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
            y[index] = y1;
            line[index].x = x1;
            line[index].y_down = y1;
            line[index].y_up = y2;
            line[index++].flag = 1; //1表示左边

            y[index] = y2;
            line[index].x = x2;
            line[index].y_down = y1;
            line[index].y_up = y2;
            line[index++].flag = -1; //-1表示右边
        }
        sort(&y[1], &y[index]); //把所有的纵坐标按从小到大排序,把1写成了0,WA一次
        sort(&line[1], &line[index]);
        build(1, 1, index-1);
        double ans=0;
        for (int i=1;i<index; i++) ///将线line从左向右遍历
            ans+=insert(1, line[i].x, line[i].y_down, line[i].y_up, line[i].flag);
        printf("%.2f\n", ans);
    }
    return 0;
}

区间更新:

/*
HDU 1255 覆盖的面积
求矩形交的面积(线段树+离散化)
给定一些矩形
被这些矩形覆盖过至少两次的区域的面积
*/
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 2010
struct Node
{
    int l,r;      ///线段树的左右整点
    int c;        ///c用来记录重叠情况
    double lf,rf; ///rf,lf分别是对应的左右真实的浮点数端点
    double cnt,more;///cnt是值被覆盖一次以上的长度,more值被覆盖两次以上的长度
}segTree[MAXN*3];
struct Line
{
    double x,y_down,y_up;
    int flag;
}line[MAXN];
///用来记录重叠情况,可以根据这个来计算,nod节点中的c

bool cmp(Line a,Line b)//sort排序的函数
{
    return a.x < b.x;
}

double y[MAXN];//记录y坐标的数组
void Build(int t,int l,int r)//构造线段树
{
    segTree[t].l=l;
    segTree[t].r=r;
    segTree[t].cnt=segTree[t].c=0;
    segTree[t].lf=y[l];
    segTree[t].rf=y[r];
    if(l+1==r)
        return;
    int mid=(l+r)>>1;
    Build(t<<1,l,mid);
    Build(t<<1|1,mid,r);//递归构造
}
void calen(int t)//计算长度
{
    if(segTree[t].c>=2)  ///该区间被覆盖两次或以上
    {
        segTree[t].more=segTree[t].cnt=segTree[t].rf-segTree[t].lf;
        return;
    }
    else if(segTree[t].c==1)  ///该区间被覆盖一次
    {
        segTree[t].cnt = segTree[t].rf-segTree[t].lf;
        if(segTree[t].l+1==segTree[t].r)
            segTree[t].more=0;
        else
           segTree[t].more = segTree[t<<1].cnt + segTree[t<<1|1].cnt;
    }
    else  ///该区间
    {
        if(segTree[t].l+1==segTree[t].r)  ///子节点
            segTree[t].more=segTree[t].cnt=0;
        else  ///非子节点
        {
            segTree[t].cnt=segTree[t<<1].cnt+segTree[ t<<1|1 ].cnt;
            segTree[t].more=segTree[t<<1].more+segTree[ t<<1|1 ].more;
        }
    }
}
void update(int t,Line line)//加入线段e,后更新线段树
{
    if( line.y_down==segTree[t].lf && line.y_up==segTree[t].rf )  ///恰好是当前的区间
        segTree[t].c += line.flag;
    else{
        if(line.y_up<=segTree[t<<1].rf)  ///需要更新的区间在当前节点的左孩子节点中
            update(t<<1,line);
        else if(line.y_down>=segTree[t<<1|1].lf)  ///需要更新的区间在当前节点的右孩子节点中
            update(t<<1|1,line);
        else  ///横跨左右两个孩子节点
        {
            Line tmp=line;
            tmp.y_up=segTree[t<<1].rf;
            update(t<<1,tmp);
            tmp=line;
            tmp.y_down=segTree[t<<1].rf;
            update(t<<1|1,tmp);
        }
    }
    calen(t);
}
int main()
{
    int i,n,t,T;
    double x1,y1,x2,y2;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        t=1;
        for(i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            line[t].x=x1;
            line[t].y_down=y1;
            line[t].y_up=y2;
            line[t].flag=1;
            y[t++]=y1;

            line[t].x=x2;
            line[t].y_down=y1;
            line[t].y_up=y2;
            line[t].flag=-1;
            y[t++]=y2;
        }
        sort(line+1,line+t,cmp);
        sort(y+1,y+t);
        Build(1,1,t-1);
        double res=0;
        for(i=1;i<t;i++)
        {
            update(1,line[i]);
            res += segTree[1].more*(line[i+1].x-line[i].x);
        }
        printf("%.2lf\n",res);
    }
    return 0;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值