覆盖的面积 HDU - 1255(线段树-矩形的交面积)

题目
给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.
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
这个题和并面积类似,就是把len换成俩len1和len2,然后pushup的时候再改变一下,若不理解,请先理解并面积
这个题好像有问题,题目上说的是给左上角和右下角的点,样例给的是左下角和右上角的点(经过测试,样例是对的)

#include <iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
typedef long long LL;
const int N=1e4+7;
vector<double>q;
struct edge    //存边的信息
{
    double x,y1,y2;
    int k;
} ma[N*2];
struct node    //建线段树
{
    int l,r;
    int cnt;
    double len1,len2;
} tr[N*8];
int cmp(struct edge a,struct edge b)  //以x排序
{
    return a.x<b.x;
}
int find(double x)     //离散化,,找原来y的大小
{
    return lower_bound(q.begin(),q.end(),x)-q.begin();
}
void pushup(int u)    //更新len,
{
    if(tr[u].cnt>=2)   //这个区间被覆盖俩次的时候,求len2的值
    {
        tr[u].len2=q[tr[u].r+1]-q[tr[u].l];
    }
    else if(tr[u].cnt==1)  //如果这个区间被覆盖一次
    {                      //那么再加上他的俩个儿子被覆盖一次的长度,就是他覆盖俩次的长度
        tr[u].len2=tr[u<<1].len1+tr[u<<1|1].len1;
    }
    //如果这个区间被覆盖0次,那么再加上他的俩个儿子被覆盖俩次次的长度,就是他覆盖俩次的长度
    else tr[u].len2=tr[u<<1].len2+tr[u<<1|1].len2; 
    if(tr[u].cnt>=1)  //更新覆盖一次的长度
    {
        tr[u].len1=q[tr[u].r+1]-q[tr[u].l];
    } //如果这个区间没有被覆盖,那么加上他俩儿子被覆盖一次的长度就是他被覆盖一次的长度
    else tr[u].len1=tr[u<<1].len1+tr[u<<1|1].len1;
}
void build(int u,int l,int r)  //建树
{    
    tr[u]= {l,r,0,0,0};
    if(l!=r)
    {
        int mid=l+r>>1;
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
    }
}
void change(int u,int l,int r,int k) 
{ 
    if(tr[u].l>=l&&tr[u].r<=r)
    {
        tr[u].cnt+=k;   //更新cnt
        pushup(u);
        return ;
    }
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid)change(u<<1,l,r,k);
    if(r>mid)change(u<<1|1,l,r,k);
    pushup(u);
    return ;
}
int main()
{
    int n,T=1;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        q.clear();
        scanf("%d",&n);
        for(int i=0,j=0; i<n; i++)
        {
            double x1,x2,y1,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            ma[j++]= {x1,y1,y2,1};
            ma[j++]= {x2,y1,y2,-1};
            q.push_back(y1);
            q.push_back(y2);
        }
        sort(q.begin(),q.end());   //离散化,去重
        unique(q.begin(),q.end());
        build(1,0,q.size()-2); //以y的区间来建线段树,
        //总共是size()-1个点那么就有size()-2个区间,0,代表q[0]到q[1]的区间,q代表q[1]到q[2]的
        //区间,,,q是离散化后的y
        
        sort(ma,ma+2*n,cmp);
        double ans=0;
        for(int i=0; i<2*n; i++)
        {
            if(i>0)ans+=tr[1].len2*(ma[i].x-ma[i-1].x);
        //    printf("find ===%d  %d\n",find(ma[i].y1),find(ma[i].y2)-1);
            change(1,find(ma[i].y1),find(ma[i].y2)-1,ma[i].k);//这里的y2找到离散化后的点后要-1,因为传递的是区间
        }                                                 //结合上面那个build那里理解,,pushup里面用的时候会+1
        printf("%.2lf\n",ans);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值