扫描线 & 线段树 (求解矩形面积并)
题目描述
求 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 【模板】扫描线 & 矩形面积并

浙公网安备 33010602011771号