【模板】扫描线
扫描线维护面积并的学习笔记。
思想很好理解,但有几个细节比较复杂所以直到今天才写这道题。
一个是点转线。输入的是点坐标,但线段树维护的是基础线段的集合,所以这其中涉及到离散化和点转线的过程。离散化是对点坐标进行离散化,而点 [l,r] 对应的线段是 [l,r-1] ,而线段 [l,r] 对应的长度是 a[r+1]-a[l] 。这就完成了点转线。
另一个是线段树。我一直在思考它是怎么做到写一个数据结构,支持区间加减和区间查询非零元素个数,就很玄学。今天琢磨了一下发现这类题目有一个特性,加一和减一操作是对应的,也就是说把两个操作拆开之后分配到的节点是完全一样的,既然如此就没有必要搞lazy标记了,加减时找到对应的节点进行操作即可。询问嘛由于它只会查询全局的答案,直接输出根节点的答案即可。
其它就还比较好写。有一道一样的题,双倍经验。
#include<bits/stdc++.h>
//#define feyn
#define int long long
const int N=400010;
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
int m,b[N<<1],cnt,top;
struct line{
int l,r,val,h;
}a[N<<1];
inline bool operator <(line s1,line s2){
return s1.h<s2.h;
}
#define lc (wh<<1)
#define rc (wh<<1|1)
#define mid (t[wh].l+t[wh].r>>1)
struct node{
int l,r,cnt,data;
}t[N<<2];
inline void pushnow(int wh,int val){
t[wh].cnt+=val;
if(t[wh].cnt)t[wh].data=b[t[wh].r+1]-b[t[wh].l];
else t[wh].data=t[lc].data+t[rc].data;
}
inline void build(int wh,int l,int r){
t[wh].l=l,t[wh].r=r;
if(l==r)return;
build(lc,l,mid);
build(rc,mid+1,r);
}
inline void change(int wh,int wl,int wr,int val){
if(wl<=t[wh].l&&t[wh].r<=wr){
pushnow(wh,val);return;
}
if(wl<=mid)change(lc,wl,wr,val);
if(wr>mid)change(rc,wl,wr,val);
pushnow(wh,0);
}
#undef lc
#undef rc
#undef mid
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
#endif
read(m);int x1,x2,y1,y2;
for(int i=1;i<=m;i++){
read(x1);read(y1);read(x2);read(y2);
a[++top]=(line){x1,x2,1,y1};
a[++top]=(line){x1,x2,-1,y2};
b[++cnt]=x1;b[++cnt]=x2;
}
sort(a+1,a+top+1);
sort(b+1,b+cnt+1);
int n=unique(b+1,b+cnt+1)-b-1,ans=0;
build(1,1,n-1);
for(int i=1;i<top;i++){
int ll=a[i].l,rr=a[i].r;
ll=upper_bound(b+1,b+n+1,ll)-b-1;
rr=upper_bound(b+1,b+n+1,rr)-b-2;
change(1,ll,rr,a[i].val);
ans+=t[1].data*(a[i+1].h-a[i].h);
}
printf("%lld",ans);
return 0;
}
一如既往,万事胜意