用于处理矩形覆盖问题,因为是线段树实现所以拥有 $O(nlogn)$ 的复杂度。

主要思想是虚拟出一条平行与 x 或 y 轴的无限长的线,我们称之为扫描线。一路扫过去,如果发现触碰到矩形的边(被当前扫描线完全覆盖)就停下进行相关操作。

1

“对于点的坐标太大,不就变成暴力一样的的东西啦?”

对于这种情况我们两种解决方法,一种是离散化,一种是动态开点。个人认为离散化做起来比较方便。

因为我们只有在扫描到线的时候才需要处理信息,所以相邻两线之间的信息是不会发生改变的。我们可以直接进行离散化。

2

对于线段树,我们大部分情况维护一个 cnt 为当前区间被几个矩形覆盖,以及一个 len 表示当前区间被覆盖的区间长度。

所以我们扫到一条线,就可以对这个区间的 cnt 进行修改,再由 cnt 对 len 修改,并且把 len 向上更新。对此我们把一个矩形的入边的权值赋为 1 ,出边赋为 -1,就可以实现修改。

于是有 pushup 函数:

	void pushup(int i){
		if(t[i].cnt) t[i].len=(rk[t[i].r+1]-rk[t[i].l]);
		else if(t[i].l==t[i].r) t[i].len=0;//防止越界,不然会RE
		else t[i].len=(t[i<<1].len+t[i<<1|1].len);
	}

需要注意的是我们的cnt是不允许 pushdown 的,这样我们在出边的修改操作中无法删除 pushdown 下去的 cnt 所以会出现错误。一般来说我们需要的只有线段树根的 cnt 和 len ,所以只需要 pushup 即可。

3

但是我们发现在 pushup 函数中我们对于被完全覆盖的区间,是用了其区间右端点+1的离散化前的值进行计算。这是因为在线段树的叶节点上,线段树上的区间是 $[l,l]$ 但是这样的区间在几何中长度为 0 了,所以我们令区间 $[l,r]$ 表示的区间实际为 $[rk[l],rk[r+1]]$ (rk 是离散化数组)就可以解决这个问题。

所以我们只需建树时 $build(1,1,tot-1)$ (tot是去重后) 即可。

4

最后需要提及一下边界问题,有些人会觉得疑惑,为什么一会是修改区间 $[l,r]$ 一会是修改区间 $[l,r-1]$?

其实本质上就是一个关于边界的问题,对于全开或全闭区间(存储线段时处理一下即可互相转换),比如统计矩形覆盖点数,我们使用 $[l,r]$,对于半开区间,像要用到几何长度时,我们采用 $[l,r-1]$ ,如一条线段左端点是 2,右端点是 5,那么长度应该为 $(5-2)=3$ 而不是 $(5-2+1)=4$(注意区间 $[l,r]$ 实际管辖的范围为$[rk[l],rk[r+1]]$)

题目推荐

矩形周长Picture

窗口的星星

扫描线模板

提供一份模板代码

#include<bits/stdc++.h>
using namespace std;
#define file(a) freopen(#a".in","r",stdin),freopen(#a".out","w",stdout)
#define LL long long
#define N 200000+3
long long n,tot;
LL rk[N];
struct line{
	LL y;
	LL l,r;
	long long val;
}p[N];
bool cmp_x(LL x,LL y){
	return x<y;
}
bool cmp_y(line x,line y){
	if(x.y==y.y) return x.val>y.val;
	return x.y<y.y;
}
struct seg{
	struct node{
		long long l,r,mid;
		long long cnt;
		LL len;
	}t[N<<2];
	void build(long long i,long long l,long long r){
		t[i].l=l;t[i].r=r;t[i].mid=(l+r)>>1;
		t[i].len=t[i].cnt=0;
		if(l==r) return;
		build(i<<1,l,t[i].mid);
		build(i<<1|1,t[i].mid+1,r);
	}
	void modify(long long i,LL l,LL r,long long k){
		if(l<=t[i].l&&t[i].r<=r){
			t[i].cnt+=k;
			if(t[i].cnt) t[i].len=(rk[t[i].r+1]-rk[t[i].l]);
			else if(t[i].l==t[i].r) t[i].len=0;
			else t[i].len=(t[i<<1].len+t[i<<1|1].len);
			return;
		}
		if(l<=t[i].mid) modify(i<<1,l,r,k);
		if(t[i].mid+1<=r) modify(i<<1|1,l,r,k);
		if(t[i].cnt) t[i].len=(rk[t[i].r+1]-rk[t[i].l]);
		else if(t[i].l==t[i].r) t[i].len=0;
		else t[i].len=(t[i<<1].len+t[i<<1|1].len);
	}
}t;
unsigned long long ans=0;
int main(){
	scanf("%d",&n);
	for(long long i=1;i<=n;++i){
		long long sx,sy,fx,fy;
		scanf("%lld%lld%lld%lld",&sx,&sy,&fx,&fy);
		rk[((i-1)<<1)+1]=sx;rk[((i-1)<<1)+2]=fx;
		p[((i-1)<<1)+1].y=sy;p[((i-1)<<1)+1].l=sx;p[((i-1)<<1)+1].r=fx;p[((i-1)<<1)+1].val=1;
		p[((i-1)<<1)+2].y=fy;p[((i-1)<<1)+2].l=sx;p[((i-1)<<1)+2].r=fx;p[((i-1)<<1)+2].val=-1;
	}
	n=n<<1;
	sort(rk+1,rk+1+n,cmp_x);
	tot=unique(rk+1,rk+1+n)-rk-1;
	sort(p+1,p+1+n,cmp_y);
	t.build(1,1,tot-1);
	for(long long i=1;i<n;++i){
		long long l=lower_bound(rk+1,rk+1+tot,p[i].l)-rk;
		long long r=lower_bound(rk+1,rk+1+tot,p[i].r)-rk;
		--r;
		if(l<=r) t.modify(1,l,r,p[i].val);
		ans+=1llu*(p[i+1].y-p[i].y)*t.t[1].len;
	}
	printf("%llu",ans);
	return 0;
}