扫描线 && 洛谷 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;
}