Luogu P5490 【模板】扫描线
作为给dtx的妹子讲题的交换他给我讲的
果然线段树最可爱了w
扫描线可以用来求矩形的面积并…
一个平面上有一些有重叠的矩形,求他们的并集的面积。
直接放网上的图了x
对于每个矩形,将y坐标拆成两个修改操作(插入和删除),从下到上排序;
将x坐标unique离散化,从左到右排序,用线段树维护切割出来的每条线段。
假设有一根从x轴出发,向上移动的线——也就是扫描线!
遇到修改时,就在线段树上对应区间加上或减掉线段,答案加上(当前线段长度之和)*(这个修改到下个修改的y坐标之差)。
线段树中维护什么信息?
sum[i]表示区间i的线段总长度。显然,sum[i] = sum[ls]+sum[rs]。
但是对于插入和删除,不能直接修改sum的值,因为一个线段可能被多个矩形覆盖...删除了一个之后还可能有另一个。
lazy[i]表示区间i被几个矩形覆盖。这样,修改的时候直接在lazy上++--就好了。
而且一定是先加再减,所以lazy标记一定不会为负数。
可以发现,每次的查询操作都是查询整个区间。那么只需要考虑上传,不用下传。
若lazy[i]>0,即被覆盖,则sum[i] = 区间i的长度;
否则,sum[i] = sum[ls]+sum[rs]。
注意:
最后一个修改要忽略!因为没有n+1了。
在传统的——维护点的线段树里,(1,4) = (1,2)+(3,4);
但维护线段时,线段(2,3)被忽略掉了。
所以更改一下定义:区间[L,R]对应线段(l,r+1)。这样保证了线段都是长为R-L+1的线段。
修改时,因为没法确定哪里有线段,所以从整个区间开始,两个儿子都要递归。
当完全偏离(L >= r[now]+1 || R <= l[now])时,就可以返回了;
当完全覆盖(L <= l[now] && r[now]+1 <= R)时,就打上lazy。
操作完一个节点并上传,判断lazy、更新sum的值时,
sum[now] = sum[ls] + sum[rs]可能出现越界的情况,因为叶子节点并没有儿子!
所以要么特判一下,要么再开2倍空间…
(哎...想起了第一次写线段树1的时候的事了...)
.
代码如下
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #include<algorithm> #define MogeKo qwq using namespace std; #define ls (now<<1) #define rs (now<<1|1) const int maxn = 2e5+10; int n,x_1,y_1,x_2,y_2,tot; long long ans; int X[maxn<<1]; int l[maxn<<2],r[maxn<<2],sum[maxn<<2],lazy[maxn<<2]; struct ScanLine { int l,r,h,val; bool operator < (const ScanLine & N) const { if(h == N.h) return val > N.val; return h < N.h; } } line[maxn<<1]; void build(int L,int R,int now) { l[now] = L; r[now] = R; sum[now] = lazy[now] = 0; if(L == R) return; int mid = (l[now]+r[now]) >> 1; build(L,mid,ls); build(mid+1,R,rs); } void pushup(int now) { if(lazy[now]) sum[now] = X[r[now]+1] - X[l[now]]; else if(l[now] == r[now]) sum[now] = 0; else sum[now] = sum[ls] + sum[rs]; } void modify(int L,int R,int c,int now) { if(L >= X[r[now]+1] || R <= X[l[now]]) return; if(L <= X[l[now]] && X[r[now]+1] <= R) { lazy[now] += c; pushup(now); return; } modify(L,R,c,ls); modify(L,R,c,rs); pushup(now); } int main() { scanf("%d",&n); for(int i = 1; i <= n; i++) { scanf("%d%d%d%d",&x_1,&y_1,&x_2,&y_2); line[2*i-1] = (ScanLine) {x_1,x_2,y_1,1}; line[2*i] = (ScanLine) {x_1,x_2,y_2,-1}; X[2*i-1] = x_1; X[2*i] = x_2; } n <<= 1; sort(line+1,line+n+1); sort(X+1,X+n+1); tot = unique(X+1,X+n+1)-X-1; build(1,tot-1,1); for(int i = 1; i < n; i++) { modify(line[i].l,line[i].r,line[i].val,1); ans += (long long)sum[1] * (line[i+1].h - line[i].h); } printf("%lld",ans); return 0; }