刍议线段树 3 (扫描线)
扫描线
扫描线是一种另外的思想,只是其中会运用到线段树以求优化。所以不要将扫描线简单的并为线段树的一个小拓展。
例题:
https://www.luogu.com.cn/problem/P5490
大意:求
思路:纵向分割图形
我们考虑把这些纵向矩形分割。
那么,总面积就为分割出的每一段矩形的面积和。
如上图,每扫描到一段,该段面积就是直线上覆盖的长度乘该段的宽度。
同时,我们用一个四元组
线段树则用来维护扫描线上被覆盖的长度(即纵向的长度),每次修改后,更新被覆盖长度。
更新代码如下:
void pushup(int x) { if(t[x].cnt) t[x].len = disx[t[x].r + 1] - disx[t[x].l]; //cnt>0,面积就是直线上覆盖的长度乘该段的宽度。 else t[x].len = t[x * 2].len + t[x * 2 + 1].len;//cnt=0,面积为其子节点的长度和。 }
这里拉一段李煜东的内容:
在本题我们只关心整个扫描线(线段树根节点)上被覆盖的长度。四元组又成对出现,所以线段树区间修改也是重复出现,这样就没必要下传延时标记,而采用更加简单的做法:在线段树每个节点上另加维护该节点代表的区间被矩形覆盖的长度
代码
讲累了,贴代码了
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define ll long long ll n; ll x1,y1,x2,y2; #define FOR(i,_l,_r) for(ll i=_l;i<=_r;i++) #define ls p<<1 #define rs p<<1|1 const int N=1e6+5; int X[N<<1]; ll ans; struct seg_line { ll l,r; ll h; ll mark; }line[N<<1]; bool cmp(seg_line a,seg_line b) { return a.h<b.h; } struct srg_tree { ll l,r; ll sum; ll len; }tr[N<<2]; void up(int p) { if(tr[p].sum) tr[p].len=X[tr[p].r+1]-X[tr[p].l]; else tr[p].len=tr[ls].len+tr[rs].len; } void build(int p,int l,int r) { tr[p].l=l;tr[p].r=r; tr[p].len=tr[p].sum=0; if(l==r) return; int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r); return; } void work(int p,ll L,ll R,int c) { ll l=tr[p].l;ll r=tr[p].r; if(X[r+1]<=L||X[l]>=R) return; if(L<=X[l]&&X[r+1]<=R) { tr[p].sum+=c; up(p); return; } work(ls,L,R,c); work(rs,L,R,c); up(p); } int main() { cin>>n; FOR(i,1,n) { cin>>x1>>y1>>x2>>y2; X[2*i-1]=x1;X[2*i]=x2; line[2*i-1]=(seg_line){x1,x2,y1,1}; line[2*i]=(seg_line){x1,x2,y2,-1}; } n<<=1; sort(line+1,line+n+1,cmp); sort(X+1,X+n+1); ll tot=unique(X+1,X+n+1)-X-1; build(1,1,tot-1); FOR(i,1,n-1) { work(1,line[i].l,line[i].r,line[i].mark); ans+=tr[1].len*(line[i+1].h-line[i].h); } cout<<ans; return 0; }
本来以为这篇要烂尾了,结果还是糊弄完了,以后本人要先转战
完结撒花。