扫描线 & 线段树 (求解矩形面积并)

题目描述

求 n 个四边平行于坐标轴的矩形的面积并。

输入格式

第一行一个正整数 n。

接下来 n 行每行四个非负整数 x1, y1, x2, y2,表示一个矩形的四个端点坐标为 (x1, y1),(x1, y2),(x2, y2),(x2, y1)。

输出格式

一行一个正整数,表示 n 个矩形的并集覆盖的总面积。

input:

2
100 100 200 200
150 150 250 255

output:

18000

说明

0< n <=1e5, 0<= x1< x2 <= 1e9, 0<= y1< y2 <= 1e9

AC代码

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define all(x) (x).begin(),(x).end()
#define endl '\n'
#define int long long
const int N=1e5+5;
struct event{
    int l,r,y,type;
    bool operator<(const event& tem)const{
        return y<tem.y;
    }
};
//以下是线段树部分,因为要动态维护整个区间所覆盖的长度(区间之和),所以想到线段树
struct node{
    int len,cnt;
}t[N<<2+4];//之前这里没有+4就RE了,以后开树都加上一点
vector<event> ev;
vector<int> v;
void update(int s,int e,int p,int l,int r,int typ){
    if(r<=s||e<=l)return ;
    if(l<=s&&e<=r){
        t[p].cnt+=typ;
    }else{
        int mid=e+s>>1;//注意不是l+r>>1
        update(s,mid,p<<1,l,r,typ);
        update(mid,e,p<<1|1,l,r,typ);//注意这里不能mid+1
        //如果+1,[mid,mid+1]这一段长度就直接被忽略了,现在维护的是相邻的线段而不是离散的点
    }
    if(t[p].cnt>0){
        t[p].len=v[e-1]-v[s-1];
    }
    // }else if(s==e){t[p].len=0;} //可以不写,反正递归到最后也是0
    else{
        t[p].len=t[p<<1].len+t[p<<1|1].len;
    }
}
void solve(){
    int n;cin>>n;
    rep(i,1,n){
        int x1,x2,y1,y2;cin>>x1>>y1>>x2>>y2;
        ev.push_back({x1,x2,y1,1});
        ev.push_back({x1,x2,y2,-1});
        v.push_back(x1);
        v.push_back(x2);
    }
    sort(all(v));
    v.erase(unique(all(v)),v.end());
    for(auto &e:ev){
        e.l=lower_bound(all(v),e.l)-v.begin()+1;//因为树维护的本来就是第几个x之间的关系,至于具体的值再在v映射一步即可
        e.r=lower_bound(all(v),e.r)-v.begin()+1;
    }
    sort(all(ev));

    int ans=0;
    int pre_y=ev[0].y;
    for(auto &e:ev){
        int dy=e.y-pre_y;
        ans+=dy*t[1].len;
        update(1,v.size(),1,e.l,e.r,e.type);//注意这里v.size()即可,不需要再+1
        pre_y=e.y;
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    //int _;cin>>_;while(_--)
    solve();
    return 0;
}

错误总结:
①线段树结点数目开小了
② int mid=e+s>>1
③ v.size()不用+1否则访问越界
④ update(mid,e,...),不用mid+1,这题比较特殊, 因为维护的每个点, 代表的是 连续的线段

题目来自洛谷 P5490 【模板】扫描线 & 矩形面积并

posted @ 2025-04-21 21:18  byxxx  阅读(21)  评论(0)    收藏  举报