求矩形覆盖面积

就是给定一些矩形的坐标,求出所以矩形覆盖了多大的面积
和线段覆盖的思想有点像
用扫描线+线段树来维护
用一个扫描线从左到右扫描,扫描到一个矩形的左边界时,将边界上包括的点在线段树中+1,扫描到一个矩形的右边界时,将边界上包括的点在线段树中-1
那么在某一时刻x=a时,线段树中值非0的点的数量就是x=a这条线上被覆盖的大小,总面积就是x=1~n之和

那么线段树中有值的点的数量怎么求呢?
维护两个值,lz就是lazy标记和d就是有值的点的数量
和常规线段树不同的是lz不用下传,因为lz无论多大都始终是针对这个区间的
当一个区间的lz>0时,d就是区间大小
当lz=0时,d就是线段树中两个字区间的d值和
lz不可能<0因为线段树中一直是先+后-的

代码不完整,其中add中的四个参数就是矩形对角线坐标
最后的结果为ans
代码如下

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
void putin(int x,int a1,int a2,int z)
{
    b[++tot].x=x;b[tot].a1=a1;b[tot].a2=a2;b[tot].z=z;
    if(z==1) b[tot].i=tot;else b[tot].i=tot-1;
}
void add(int x1,int x2,int y1,int y2)
{
    if(x1>x2) swap(x1,x2);
    if(y1>y2) swap(y1,y2);
    printf("%d %d %d %d\n",x1,x2,y2,y2);
    putin(x1,y1,y2,1);
    putin(x2+1,y1,y2,-1);
}
bool cnt(node x,node y){return (x.x<y.x)||((x.x==y.x&&x.z>y.z)||((x.x==y.x&&x.z==y.z&&x.a2<y.a2)||(x.x==y.x&&x.z==y.z&&x.a2==y.a2&&x.a1<y.a1)));}
void insert(int v,int i,int j,int x,int y,int z)
{
    if(i==x&&j==y)
    {
        lz[v]+=z;
        if(lz[v]==0) t[v]=t[v*2]+t[v*2+1];
        else t[v]=j-i+1;
        return;
    }
    int m=(i+j)/2;
    if(y<=m) insert(v*2,i,m,x,y,z);
    else if(x>m) insert(v*2+1,m+1,j,x,y,z);
         else insert(v*2,i,m,x,m,z),insert(v*2+1,m+1,j,m+1,y,z);
    if(lz[v]) t[v]=j-i+1;
    else t[v]=t[v*2]+t[v*2+1];
}
int main()
{
    sort(b+1,b+tot+1,cnt);
    ll ans=0;
    fo(i,1,tot)
    {
        ans-=(ll)t[1]*(ll)(b[i].x-b[i-1].x);
        if(b[i].x>n) break;
        if(b[i].x!=b[i-1].x||b[i].a2!=b[i-1].a2||b[i].z!=b[i-1].z) 
        {
            if(b[i].z==1||bz[b[i].i]==1) insert(1,1,n+1,b[i].a1,b[i].a2,b[i].z);
            bz[b[i].i]=1;
        }   
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值