例题:求解多个长方形之并的面积
https://www.acwing.com/problem/content/249/
蓝色表示长方形,红色表示扫描线
如下图所示,对于每一个横向的区间,在纵向维护线段树
根据纵向的累计长度,即可对每个横向区间求出面积
求面积的过程中,可以从左到右遍历区间(遍历除第一个点以外的分界点),遇到左边界,则将线段对应全部位置+1,右边界则-1。此时面积就是区间长度*全部大于0的位置的数量。
由于扫描线的性质(只查询节点1、加和减成对出现、cnt大于0则不需要向下递归等性质),不需要使用懒标记。只需维护当前节点的cnt和len,cnt表示当前区间整个被覆盖的次数,len表示cnt>0的区间总长度。
代码:(注意,例题中由于y存在小数,故需要离散化,并且线段树中的"点"代表一个区间)
#include<bits/stdc++.h> #define fore(x,y,z) for(LL x=(y);x<=(z);x++) #define forn(x,y,z) for(LL x=(y);x<(z);x++) #define rofe(x,y,z) for(LL x=(y);x>=(z);x--) #define rofn(x,y,z) for(LL x=(y);x>(z);x--) #define pub push_back #define all(x) (x).begin(),(x).end() #define fi first #define se second using namespace std; typedef long long LL; typedef pair<int,int> PII; typedef pair<LL,LL> PLL; const int N=100010; int T=1; struct Segment { double x,y1,y2; int k; bool operator<(const Segment &t) const { return x<t.x; } }seg[N*2];//长方形的左右边,即需要用于添加和删除的线段 struct Node { int l,r; int cnt; double len; }tr[N*8];//最多2N个点,2*N-1个区间 int n; vector<double> ys;//y离散化 int find(double y)//找到离散化后对应的下标 { return lower_bound(ys.begin(),ys.end(),y)-ys.begin(); } void pushup(int u) { if(tr[u].cnt) tr[u].len=ys[tr[u].r+1]-ys[tr[u].l];//区间右端点是y[r+1] else if(tr[u].l!=tr[u].r) { //由于modify中使用了pushup进行计算, //而没有对叶节点进行特殊,所以需要在这里判断 tr[u].len=tr[u*2].len+tr[u*2+1].len; } else tr[u].len=0; } void build(int u,int l,int r) { tr[u]={l,r,0,0}; if(l!=r) { int mid=(l+r)/2; build(u*2,l,mid); build(u*2+1,mid+1,r); } } void modify(int u,int l,int r,int k) { if(tr[u].l>=l&&tr[u].r<=r) { tr[u].cnt+=k; pushup(u); } else { int mid=(tr[u].l+tr[u].r)/2; if(l<=mid) modify(u*2,l,r,k); if(r>=mid+1) modify(u*2+1,l,r,k); pushup(u); } } void YD() { ys.clear(); int cnt=0; fore(i,1,n) { double x1,x2,y1,y2; cin>>x1>>y1>>x2>>y2; seg[cnt++]={x1,y1,y2,1}; seg[cnt++]={x2,y1,y2,-1}; ys.pub(y1);ys.pub(y2); } sort(ys.begin(),ys.end()); ys.erase(unique(ys.begin(),ys.end()),ys.end()); sort(seg,seg+2*n); build(1,0,ys.size()-2);//每个y代表其后面的区间,故不需要建立最后一个y double res=0; fore(i,0,2*n-1) { if(i>0) res+=tr[1].len*(seg[i].x-seg[i-1].x); modify(1,find(seg[i].y1),find(seg[i].y2)-1,seg[i].k); //对于n个点共有n-1个区间,可以用每一个点代表以其为左端点的区间, //故find(seg[i].y2-1),表示不修改y2后面的区间。 } cout<<"Test case #"<<T<<endl; cout<<"Total explored area: "<<fixed<<setprecision(2)<<res<<endl; cout<<endl; } int main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); while (cin>>n,n) { YD(); T++; } return 0; }View Code
标签:cnt,int,线段,tr,seg,扫描线,ys,y2,AcWing From: https://www.cnblogs.com/ydUESTC/p/16735703.html