【学习笔记】线段树 之 扫描线
I 概况
- 扫描线就是解决一些在二维平面内处理矩形相交的一系列问题。我们在这里具体地讲两个:矩形面积并以及周长并
- 若不喜,勿喷。
II 矩形面积并
- 例题模板
- 面积并相对周长来说比较简单。
- 首先我们来大致了解一下扫描线地工作原理:我们每次加入一个矩阵的操作相当于左边那条边
+1,右边那条
−1(有点类似于差分的性质)。画张图来看看:
- 相当于每次加入矩形一条边我们就去寻找与其对应的
−1边,而面积就是这两条线之间的距离
×高(相当于我们把这个图分成若干个矩形来求解)
- 对于那些
xi,yi大的要进行离散化
-
Code
#include <bits/stdc++.h>
#define pb push_back
#define int long long
using namespace std;
inline int read()
{
int sum=0,ff=1; char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') ff=-1;
ch=getchar();
}
while(isdigit(ch))
sum=sum*10+(ch^48),ch=getchar();
return sum*ff;
}
const int N=4e5+5;
int n,m,X[N],Y[N],ans;
struct nood
{
int l,r,y,inv;
inline bool friend operator < (const nood &s,const nood &t)
{
return s.y<t.y;
}
};
nood a[N];
struct Seg
{
int ok[N*4],T[N*4];
inline void up(int rt,int l,int r)
{
if(ok[rt]) T[rt]=X[r+1]-X[l];
else T[rt]=T[rt<<1]+T[rt<<1|1];
}
inline void update(int rt,int l,int r,int ll,int rr,int inv)
{
if(ll<=l&&r<=rr)
{
ok[rt]+=inv;
up(rt,l,r);
return;
}
int mid=(l+r)/2;
if(ll<=mid)
update(rt<<1,l,mid,ll,rr,inv);
if(rr>mid)
update(rt<<1|1,mid+1,r,ll,rr,inv);
up(rt,l,r);
}
};
Seg T;
signed main()
{
n=read();
for ( int i=1;i<=n;i++ )
{
int sx,sy,ex,ey;
sx=read(),sy=read();
ex=read(),ey=read();
X[i*2-1]=sx;
X[i*2]=ex;
a[i*2-1]=(nood){sx,ex,sy,1};
a[i*2]=(nood){sx,ex,ey,-1};
}
sort(X+1,X+2*n+1);
sort(a+1,a+2*n+1);
m=unique(X+1,X+2*n+1)-X-1;
for ( int i=1;i<2*n;i++ )
{
int l=lower_bound(X+1,X+m+1,a[i].l)-X;
int r=lower_bound(X+1,X+m+1,a[i].r)-X;
T.update(1,1,m,l,r-1,a[i].inv);
ans=(ans+(a[i+1].y-a[i].y)*T.T[1]);
}
printf("%lld\n",ans);
return 0;
}
III 矩形周长并
- 例题模板
- 我们对于周长并我们考虑这样去计算贡献(看图
- 我们每次只计算红,绿边的贡献。我们考虑分开计算:绿边的就是一段区间线段的个数
×长度
×2
- 那么我们要维护
5个东西
-
T[k].ok:是否被覆盖
-
T[k].num:被覆盖了几次
-
T[k].len:被覆盖的长度
-
T[k].ls:左区间是否被覆盖
-
T[k].rs:右区间是否被覆盖
- 对于
len,ok,ls,rs都是很简单地维护一下就可以了,那么对于
num我们还要减掉左区间右端点以及右区间左端点一起被覆盖地情况(因为这两条线段也会构成一条更长的线段
- 于是
T[k].num=T[k∗2].num+T[k∗2+1].num−(T[k∗2].ls&T[k∗2+1].rs)
-
Code
#include <bits/stdc++.h>
#define pb push_back
#define int long long
using namespace std;
inline int read()
{
int sum=0,ff=1; char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') ff=-1;
ch=getchar();
}
while(isdigit(ch))
sum=sum*10+(ch^48),ch=getchar();
return sum*ff;
}
const int N=4e5+5;
int n,m,X[N],Y[N],ans;
struct nood
{
int l,r,y,inv;
inline bool friend operator < (const nood &s,const nood &t)
{
if(s.y==t.y) return s.inv>t.inv;
return s.y<t.y;
}
};
nood a[N];
struct Seg
{
struct seg
{
int num,len,ls;
int rs,ok;
};
seg T[N*4];
inline void up(int rt,int l,int r)
{
if(T[rt].ok)
{
T[rt].len=X[r+1]-X[l];
T[rt].num=1;
T[rt].ls=T[rt].rs=1;
}
else
{
T[rt].len=T[rt<<1].len+T[rt<<1|1].len;
T[rt].num=T[rt<<1].num+T[rt<<1|1].num-(T[rt<<1].rs&&T[rt<<1|1].ls);
T[rt].ls=T[rt<<1].ls;
T[rt].rs=T[rt<<1|1].rs;
}
}
inline void update(int rt,int l,int r,int ll,int rr,int inv)
{
if(ll<=l&&r<=rr)
{
T[rt].ok+=inv;
up(rt,l,r);
return;
}
int mid=(l+r)/2;
if(ll<=mid)
update(rt<<1,l,mid,ll,rr,inv);
if(rr>mid)
update(rt<<1|1,mid+1,r,ll,rr,inv);
up(rt,l,r);
}
};
Seg T;
signed main()
{
n=read();
for ( int i=1;i<=n;i++ )
{
int sx,sy,ex,ey;
sx=read(),sy=read();
ex=read(),ey=read();
X[i*2-1]=sx;
X[i*2]=ex;
a[i*2-1]=(nood){sx,ex,sy,1};
a[i*2]=(nood){sx,ex,ey,-1};
}
sort(X+1,X+2*n+1);
sort(a+1,a+2*n+1);
m=unique(X+1,X+2*n+1)-X-1;
int laspos=0;
for ( int i=1;i<=2*n;i++ )
{
int l=lower_bound(X+1,X+m+1,a[i].l)-X;
int r=lower_bound(X+1,X+m+1,a[i].r)-X;
T.update(1,1,m,l,r-1,a[i].inv);
ans=ans+abs(T.T[1].len-laspos);
ans=ans+2*T.T[1].num*(a[i+1].y-a[i].y);
laspos=T.T[1].len;
}
printf("%lld\n",ans);
return 0;
}