HDU1255-覆盖的面积(线段树+扫描线——面积交)

覆盖的面积

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5801    Accepted Submission(s): 2899


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


 

Input
输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=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
 

大致和HDU1542求面积并的方法没太大区别,在结构体中加了一个变量存储覆盖大于一次的长度,然后在求父节点的时候多了几组判断条件

依然是扫描线的思想,只是记录一下覆盖两次或两次以上的边,然后多更新了一个t[rt].ss(覆盖多次的边的长度),最后求面积由覆盖多次的边求

具体看代码

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int inf=0x3f3f3f3f;
const int N=2200;
struct edge
{
    double l,r,h;
    int f;
    edge() {}
    edge(double x1,double x2,double y,double op)
    {
        l=x1,r=x2,h=y,f=op;
    }
    bool operator < (const edge&A)const
    {
        return h<A.h;
    }
} e[N];
struct node
{
    int cnt;
    double s,ss;
} t[N<<2];
double pos[N];
void pushdown(int rt,int le,int ri)
{
    if(t[rt].cnt)//全部覆盖
        t[rt].s=pos[ri+1]-pos[le];
    else if(le==ri)//为一个点,长度为0
        t[rt].s=0;
    else//既不全部覆盖,也不是点,由两边区间得出
        t[rt].s=t[rt<<1].s+t[rt<<1|1].s;
    if(t[rt].cnt>1)
        t[rt].ss=pos[ri+1]-pos[le];
    else if(le==ri)
        t[rt].ss=0;
    else if(t[rt].cnt==1)
        t[rt].ss=t[rt<<1].s+t[rt<<1|1].s;
        /*
        cnt==1代表从le到ri这段(完整的)长度从初始到现在被覆盖了一次,所以在这段区间中被覆盖两次
      的长度等于左右子节点中被覆盖一次的长度之和(根节点的覆盖和子节点的覆盖肯定不在同一段线段上)
        */
    else
        t[rt].ss=t[rt<<1].ss+t[rt<<1|1].ss;
}
void update(int x,int y,int l,int r,int rt,int val)
{
    if(x<=l&&y>=r)
    {
        t[rt].cnt+=val;
        pushdown(rt,l,r);
        return;
    }
    int m=(l+r)>>1;
    if(x<=m)
        update(x,y,l,m,rt<<1,val);
    if(y>m)
        update(x,y,m+1,r,rt<<1|1,val);
    pushdown(rt,l,r);
}
int main()
{
    int n,Caes;
    double x1,x2,y1,y2;
    scanf("%d",&Caes);
    while(Caes--)
    {
        scanf("%d",&n);
        mem(t,0);
        int tot=0;
        for(int i=0; i<n; i++)
        {
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            pos[tot]=x1;
            e[tot++]=edge(x1,x2,y1,1);
            pos[tot]=x2;
            e[tot++]=edge(x1,x2,y2,-1);
        }
        sort(pos,pos+tot);
        sort(e,e+tot);
        int m=unique(pos,pos+tot)-pos;
        double ans=0;
        for(int i=0; i<tot; i++)
        {
            int x=lower_bound(pos,pos+m,e[i].l)-pos;
            int y=lower_bound(pos,pos+m,e[i].r)-pos-1;
            update(x,y,0,m,1,e[i].f);
            ans+=(e[i+1].h-e[i].h)*t[1].ss;
        }
        printf("%.2f\n",ans);
        /*
         这里注意下扫描线段时r-1:int R=search(s[i].l,pos,m)-1;
         计算底边长时r+1:if(mark[n])sum[n]=pos[right+1]-pos[left];
         解释:假设现在有一个线段左端点是l=0,右端点是r=m-1
         则我们去更新的时候,会算到sum[1]=pos[mid]-pos[left]+pos[right]-pos[mid+1]
         这样的到的底边长sum是错误的,why?因为少算了mid~mid+1的距离,由于我们这利用了
         离散化且区间表示线段,所以mid~mid+1之间是有长度的,比如hash[3]=1.2,pos[4]=5.6,mid=3
         所以这里用r-1,r+1就很好理解了
         */
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值