扫描线 && 洛谷 P5490 【模板】扫描线(线段树)

传送门


扫描线

什么是扫描线?

在一个二维平面上有许多的点,那一根水平或者竖直的直线将其切割。

用途?

降维。
可以把二维问题转变成一维处理。
解决许多数据结构问题。
lxl的重要思路:一维问题--->二维平面--->扫描线降维。

应用条件?

离线。

实现?

线段树或者树状数组。
树状数组常数比较小所以:区间修改单点查询可以改成单点修改区间查询问题-->树状数组实现。
但是这个题因为是区间修改区间查询所以用线段树。

注意事项?

线段树一定要开足够大小。
应手算一下,有时开四倍空间是不够的(例如本题需要开六倍)。
建议动态开点。

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=4e5+5;
struct node{
	int x,xx,y,v;
}a[maxn];
struct Node{
	long long len;
	int v,l,r;
}d[maxn*6];
int n,m,b[maxn],cnt;
long long ans;
bool cmp(node a,node b){
	return a.y<b.y;
}
void pushup(int id){
	if(d[id].v) d[id].len=b[d[id].r+1]-b[d[id].l];
	else d[id].len=d[id*2].len+d[id*2+1].len;
}
void build(int id,int l,int r){
	d[id].l=l;
	d[id].r=r;
	if(l==r) return;
	int mid=(l+r)/2;
	build(id*2,l,mid);
	build(id*2+1,mid+1,r);
}
void update(int id,int l,int r,int x,int y,int v){
	if(x<=l&&r<=y){
		d[id].v+=v;
		pushup(id);
		return;
	}
	int mid=(l+r)/2;
	if(x<=mid) update(id*2,l,mid,x,y,v);
	if(y>mid) update(id*2+1,mid+1,r,x,y,v);
	pushup(id);
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].x>>a[i].y>>a[i+n].xx>>a[i+n].y;
		a[i].xx=a[i+n].xx;
		a[i+n].x=a[i].x;
		a[i].v=1;
		a[i+n].v=-1;
		b[++cnt]=a[i].x;
		b[++cnt]=a[i].y;
		b[++cnt]=a[i+n].xx;
		b[++cnt]=a[i+n].y;
	}
	sort(b+1,b+cnt+1);
	m=unique(b+1,b+cnt+1)-b-1;
	for(int i=1;i<=n;i++){
		a[i].x=lower_bound(b+1,b+m+1,a[i].x)-b;
		a[i].y=lower_bound(b+1,b+m+1,a[i].y)-b;
		a[i].xx=lower_bound(b+1,b+m+1,a[i].xx)-b;
		a[i+n].y=lower_bound(b+1,b+m+1,a[i+n].y)-b;
		a[i+n].x=a[i].x;
		a[i+n].xx=a[i].xx;
	}
	sort(a+1,a+2*n+1,cmp);
	build(1,1,m);
	for(int i=1;i<=2*n;i++){
		ans+=1ll*(b[a[i].y]-b[a[i-1].y])*d[1].len;
		update(1,1,m,a[i].x,a[i].xx-1,a[i].v);
	}
	cout<<ans;
    return 0;
}
posted @ 2021-09-15 09:32  尹昱钦  阅读(90)  评论(0编辑  收藏  举报