扫描线
一般如果是从左往右扫的话,首先用结构体表示图形(长方形或正方形)的左右两边;
struct D{
int f,X,Y1,Y2;
}du[N*2];
f 表示该边时入边还是出边,入边为 1 , 出边表示 -1 ;
X 表示该边的x坐标,Y1 表示该边的上边y坐标,Y2 表示该边的下边y坐标;
如图:
这个还没完,可以发现Y的取值范围为1-1e9,因为我们线段树维护的是Y(这个后面讲),所以肯定爆空间,图形数量是1e5,所以离散化Y是必须的一步;
Y离散化完了,就再把结构体du[]以X由小到大排序,然后从左往右扫;
再说线段树维护什么;
struct Node{
int l,r,len,lz;
}tr[N*8];
l,r意义不变,len表示的就是该区间的长度,lz表示该区间被加入的次数(也就是上面的入边和出边 f );
最最最重要也最难的部分来了:
void pp(int k){
if(tr[k].lz) tr[k].len=val[tr[k].r+1]-val[tr[k].l];
else if(tr[k].l==tr[k].r) tr[k].len=0;
else tr[k].len=tr[ls].len+tr[rs].len;
}
记住,扫描线区间修改操作不需要往下转标记,也就说不需要每个叶子结点的值都改变;
所以只要pp,向上传就可以;
当lz有值时,就是说该区间有边加入时,那么该区间的长度len直接等于r-l(注意l,r以被离散化,val表示离散化前的原值),这里还有个r+1,也是非常非常重要难理解的一部分,之后再讲;
当lz没有值时,只要加上左右区间的长度即可;
我们查询的时候,一般是直接查全局的长度,tr[1].len;
所以最后我们只要区间修改,和直接查询即可;
LL ans=0;
for(int i=1;i<tot;i++){
update(du[i].Y1,du[i].Y2-1,du[i].f,1);
ans+=1LL*tr[1].len*(du[i+1].X-du[i].X);
}
修改的值就是du[i].f,表示该边是加入还是出去,这里为啥是Y2-1,这跟前面为啥是r+1有联系;
现在讲前面最重要,最难理解的黄色部分:一切的一切都是离散化导致的;
假设,l=1,r=m,我们要在区间[1,m]中加1,是不是直接len=len[d]-len[l]+len[r]-len[d+1];
这样算出来的len是错的,为啥?因为d–d+1之间的距离我们没算,这也是线段树维护的Y的距离而不是传统的线段树的区别了;
全部代码来自这道题:
代码:
#include<bits/stdc++.h>
#define LL unsigned long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=100010;
const int M=50100;
const LL mod=10007;
int n,x_1[N],x_2[N],y_1[N],y_2[N],a[N*2],cnt,tot,val[N*2];
struct D{
int f,X,Y1,Y2;
}du[N*2];
struct Node{
int l,r,len,lz;
}tr[N*8];
void build(int l,int r,int k){
tr[k].l=l,tr[k].r=r;
if(l==r) return;
int d=(l+r)>>1;
build(l,d,ls);
build(d+1,r,rs);
}
void pp(int k){
if(tr[k].lz) tr[k].len=val[tr[k].r+1]-val[tr[k].l];
else if(tr[k].l==tr[k].r) tr[k].len=0;
else tr[k].len=tr[ls].len+tr[rs].len;
}
void update(int l,int r,int w,int k){
if(tr[k].l>=l&&tr[k].r<=r){
tr[k].lz+=w;
pp(k);
return;
}
int d=(tr[k].l+tr[k].r)>>1;
if(l<=d) update(l,r,w,ls);
if(r>d) update(l,r,w,rs);
pp(k);
}
bool cmp(D p,D q){
if(p.X==q.X) return p.f>q.f;
return p.X<q.X;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&x_1[i],&y_1[i],&x_2[i],&y_2[i]);
a[++cnt]=y_1[i],a[++cnt]=y_2[i];
}
sort(a+1,a+cnt+1);
cnt=unique(a+1,a+cnt+1)-a-1;
for(int i=1;i<=n;i++){
int p1=lower_bound(a+1,a+cnt+1,y_1[i])-a;
int p2=lower_bound(a+1,a+cnt+1,y_2[i])-a;
val[p1]=y_1[i],val[p2]=y_2[i];
du[++tot].f=1,du[tot].X=x_1[i],du[tot].Y1=p1,du[tot].Y2=p2;
du[++tot].f=-1,du[tot].X=x_2[i],du[tot].Y1=p1,du[tot].Y2=p2;
}
sort(du+1,du+tot+1,cmp);
build(1,cnt-1,1);
LL ans=0;
for(int i=1;i<tot;i++){
update(du[i].Y1,du[i].Y2-1,du[i].f,1);
ans+=1LL*tr[1].len*(du[i+1].X-du[i].X);
}
printf("%lld\n",ans);
return 0;
}