线段树 之 扫描线

【学习笔记】线段树 之 扫描线

  • 一篇简单易懂的扫描线讲解

I \mathrm{I} 概况

  • 扫描线就是解决一些在二维平面内处理矩形相交的一系列问题。我们在这里具体地讲两个:矩形面积并以及周长并
  • 若不喜,勿喷。

I I \mathrm{II} 矩形面积并

  • 例题模板
  • 面积并相对周长来说比较简单。
  • 首先我们来大致了解一下扫描线地工作原理:我们每次加入一个矩阵的操作相当于左边那条边 + 1 +1 ,右边那条 1 -1 (有点类似于差分的性质)。画张图来看看:

图1-1

  • 相当于每次加入矩形一条边我们就去寻找与其对应的 1 -1 边,而面积就是这两条线之间的距离 × \times 高(相当于我们把这个图分成若干个矩形来求解)
  • 对于那些 x i , y i x_i,y_i 大的要进行离散化
  • C o d e \mathrm{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];//因为是离散化后的下标,所以是加X[]
		else T[rt]=T[rt<<1]+T[rt<<1|1];
		//ok[]:是否被覆盖过
	}
	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;
}

I I I \mathrm{III} 矩形周长并

  • 例题模板
  • 我们对于周长并我们考虑这样去计算贡献(看图
    图1-2
  • 我们每次只计算红,绿边的贡献。我们考虑分开计算:绿边的就是一段区间线段的个数 × \times 长度 × 2 \times 2
  • 那么我们要维护 5 5 个东西
    • T [ k ] . o k T[k].ok :是否被覆盖
    • T [ k ] . n u m T[k].num :被覆盖了几次
    • T [ k ] . l e n T[k].len :被覆盖的长度
    • T [ k ] . l s T[k].ls :左区间是否被覆盖
    • T [ k ] . r s T[k].rs :右区间是否被覆盖
  • 对于 l e n , o k , l s , r s len,ok,ls,rs 都是很简单地维护一下就可以了,那么对于 n u m num 我们还要减掉左区间右端点以及右区间左端点一起被覆盖地情况(因为这两条线段也会构成一条更长的线段
  • 于是 T [ k ] . n u m = T [ k 2 ] . n u m + T [ k 2 + 1 ] . n u m ( T [ k 2 ] . l s & T [ k 2 + 1 ] . r s ) T[k].num=T[k*2].num+T[k*2+1].num-(T[k*2].ls\And T[k*2+1].rs)
  • C o d e \mathrm{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);
//		cout<<"len="<<T.T[1].len<<endl;
//		cout<<"num="<<T.T[1].num<<endl;
		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;
}
/*
*/
		

猜你喜欢

转载自blog.csdn.net/wangyiyang2/article/details/105666529
今日推荐