hdu 1255 线段树+离散化+扫描线 (矩形面积交

本文介绍了一种计算平面上多个矩形至少两次覆盖区域面积的算法。通过使用线段树来跟踪不同矩形的覆盖情况,该算法可以高效地解决这一问题。输入包括矩形数量及其坐标,输出为保留两位小数的覆盖面积。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



链接:戳这里


覆盖的面积

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)

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
 


思路:

在矩形面积并的基础上,加一个存至少交两次的权值的数组

分析一下矩形面积并的时候,tr[root]存的是当前x轴区间是否被覆盖,这里注意到一个点,就是tr[root]=1只能确定[l,r]被完全覆盖至少一次,但是子区间可能有覆盖超过两次的。简单分析下吧

tr[root]>=2:区间[l,r]被完全覆盖至少两次以上。即two[root]=one[root]=one[root*2]+one[root*2+1]

tr[root]=1:当前区间被完全覆盖至少一次,那么覆盖一次的答案one[root]=X[r+1]-X[l],覆盖两次怎么算呢。我们已知整个区间[l,r]至少被覆盖一次,那么子区间至少覆盖一次的[l,mid],[mid+1,r] 也就是子区间至少被覆盖两次了。这里画画线段树就出来了。two[root]=one[root*2]+one[root*2+1]

tr[root]=0:当前区间[l,r]没有被完全覆盖过,所以更新子区间的答案就可以了


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#include<cmath>
#include<bitset>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
typedef long double ld;
#define INF (1ll<<60)-1
#define Max 1e9
using namespace std;
struct Line{
    double x1,x2,y;
    int f;
    Line(double x1=0,double x2=0,double y=0,int f=0):x1(x1),x2(x2),y(y),f(f){}
    bool operator < (const Line &a)const{
        if(y==a.y) return f>a.f;
        return y<a.y;
    }
}L[50010];
double X[50010];
double one[100010],two[100010];
int tr[100010];
void build(int root,int l,int r){
    if(l==r) {
        tr[root]=0;
        one[root]=0;
        two[root]=0;
        return ;
    }
    int mid=(l+r)/2;
    build(root*2,l,mid);
    build(root*2+1,mid+1,r);
    one[root]=one[root*2]+one[root*2+1];
    two[root]=two[root*2]+two[root*2+1];
    tr[root]=tr[root*2]+tr[root*2+1];
}
void pushup(int root,int l,int r){
    if(tr[root]>=2){
        two[root]=one[root]=X[r+1]-X[l];
        return ;
    } else if(tr[root]==1){
        one[root]=X[r+1]-X[l];
        if(l==r) two[root]=0;
        else two[root]=one[root*2]+one[root*2+1];
    } else {
        if(l==r) one[root]=two[root]=0;
        else {
            two[root]=two[root*2]+two[root*2+1];
            one[root]=one[root*2]+one[root*2+1];
        }
    }
}
void update(int root,int l,int r,int x,int y,int v){
    if(x==l && y==r) {
        //printf("l=%d r=%d\n",l,r);
        tr[root]+=v;
        pushup(root,l,r);
        return ;
    }
    int mid=(l+r)/2;
    if(y<=mid) update(root*2,l,mid,x,y,v);
    else if(x>mid) update(root*2+1,mid+1,r,x,y,v);
    else {
        update(root*2,l,mid,x,mid,v);
        update(root*2+1,mid+1,r,mid+1,y,v);
    }
    pushup(root,l,r);
}
int main(){
    int T,n,m,cnt;
    double x1,y1,x2,y2;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        m=0;
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            L[++m]=Line(x1,x2,y1,1);
            X[m]=x1;
            L[++m]=Line(x1,x2,y2,-1);
            X[m]=x2;
        }
        sort(L+1,L+m+1);
        sort(X+1,X+m+1);
        int cnt=unique(X+1,X+m+1)-(X+1);
        build(1,1,cnt);
        double ans=0;
        int l=lower_bound(X+1,X+cnt+1,L[1].x1)-X;
        int r=lower_bound(X+1,X+cnt+1,L[1].x2)-X-1;
        update(1,1,cnt,l,r,L[1].f);
        //printf("%d %d\n",l,r);
        for(int i=2;i<=m;i++){
            if(i>2) ans+=(L[i].y-L[i-1].y)*two[1];
            l=lower_bound(X+1,X+cnt+1,L[i].x1)-X;
            r=lower_bound(X+1,X+cnt+1,L[i].x2)-X-1;
            if(l<=r) update(1,1,cnt,l,r,L[i].f);
            //printf("%d %d\n",l,r);
        }
        printf("%.2f\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值