【模板】扫描线

link

扫描线维护面积并的学习笔记。

思想很好理解,但有几个细节比较复杂所以直到今天才写这道题。

一个是点转线。输入的是点坐标,但线段树维护的是基础线段的集合,所以这其中涉及到离散化和点转线的过程。离散化是对点坐标进行离散化,而点 [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;
}
posted @ 2022-07-07 17:23  Feyn618  阅读(22)  评论(0编辑  收藏  举报