线段树——扫描线

扫描线

用于求矩形面积并,矩形周长并

矩形面积并

洛谷P5490
图一

以下图片来自洛谷题解(难看的就是我自己画的qwq)


图二

以上是两个矩形,他们的面积并就是他们的面积取并


图三

考虑把这两个矩形的面积并转化成 A B C 三个矩形的面积(即把重叠问题转化为无重叠问题)


图四

如何求 A B C 的面积?

高是好求的,把四个高依次相减,就是 A B C 的高

如何求底 ?

如图四,我们把从左到右的三条线段记作 p1 p2 p3 ,那么矩形 A 的底即为 p1+p2 , 矩形 B 的底即为 p1+p2+p3 , 矩形 C 的底即为 p2+p3

那么求 A B C 的底 ,实际上就是判断什么时候用 p1 p2 p3 计算

扫描线

扫描线其实就是用来求底的

此时需要引入一个概念:出边入边

一个矩形的下底就是一条入边,一个矩形的上底就是一条出边

定义 T1 T2 T3 判断是否用 p1 p2 p3 计算当前底 , 当扫描线遇到入边时 , T + 1 , 当扫描线遇到出边时 , T - 1 , T > 0 , 则说明它对应的 P 应被计算

线段树

还是这个图 , 因为线段树的一个节点表示的是一个区间范围 , 而底的计算就是计算 p1 p2 p3 的组合 , 相当于是区间和

我们把 p1 p2 p3 看作叶子节点 , 当扫描线扫到一条入边时 , 则向区间加这条线段 ;当扫描线扫到一条出边时 ,则向区间减这条线段

复杂度 O(mlogn)

看不懂没关系 , 来看看代码吧 !

代码

步骤:
①读取所有矩形,记录入边出边

struct node{
	ll xl,xr,y;//边的左x坐标,右x坐标, y坐标
	ll v;//记录是入边(+1)还是出边(-1)
	bool operator < (const node &x) const{
        return y<x.y;
    }
}s[N<<1];

ll cnt=0;
while(n--){
		ll x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		s[++cnt]=(node){x1,x2,y1,1};
		x[cnt]=x1;
		s[++cnt]=(node){x1,x2,y2,-1};
		x[cnt]=x2;
	}

②对所有边按 y 轴排序(扫描线是从下往上依次扫的)

	sort(s+1,s+cnt+1);//上面有重载运算符

③对 x 轴离散化(看看数据范围)

        sort(x+1,x+cnt+1);
	int num=unique(x+1,x+cnt+1)-(x+1);
	ll ans=0;
	for(int i=1;i<cnt;i++){
		int L,R;
		L=lower_bound(x+1,x+num+1,s[i].xl)-x;
		R=lower_bound(x+1,x+num+1,s[i].xr)-x;

④更新线段树

//应该不用建树吧 ,emmm……,也没有啥好初始的吧
void pushup(ll p,ll pl,ll pr){
	if(tag[p]){//如果T>0
		lenth[p]=x[pr]-x[pl]; //计算宽度
	}
	else if(pl+1==pr)lenth[p]=0; //叶子节点(这里的叶子节点是一个一个块,详见图四)
	else lenth[p]=lenth[ls(p)]+lenth[rs(p)]; 
}
void update(ll p,ll L,ll R,ll pl,ll pr,ll d){
	if(L<=pl&&R>=pr){
		tag[p]+=d;
		pushup(p,pl,pr);
		return;
	}
	if(pl+1==pr)return;
	int mid=(pl+pr)>>1;
	if(L<=mid) update(ls(p),L,R,pl,mid,d);
	if(R>mid) update(rs(p),L,R,mid,pr,d);//注意不是mid+1,理由同上
	pushup(p,pl,pr);
}

update(1,L,R,1,num,s[i].v);

⑤面积求和

ans+=lenth[1]*(s[i+1].y-s[i].y);

总代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100005
ll ls(int x){return x<<1;}
ll rs(int x){return x<<1|1;}
struct node{
	ll xl,xr,y;
	ll v;
	bool operator < (const node &x) const{
        return y<x.y;
    }
}s[N<<1];
ll x[N<<1],tag[N<<2],lenth[N<<1];
void pushup(ll p,ll pl,ll pr){
	if(tag[p]){
		lenth[p]=x[pr]-x[pl];
	}
	else if(pl+1==pr)lenth[p]=0;
	else lenth[p]=lenth[ls(p)]+lenth[rs(p)];
}
void update(ll p,ll L,ll R,ll pl,ll pr,ll d){
	if(L<=pl&&R>=pr){
		tag[p]+=d;
		pushup(p,pl,pr);
		return;
	}
	if(pl+1==pr)return;
	int mid=(pl+pr)>>1;
	if(L<=mid) update(ls(p),L,R,pl,mid,d);
	if(R>mid) update(rs(p),L,R,mid,pr,d);
	pushup(p,pl,pr);
}
int main(){
	int n;
	ll cnt=0;
	cin>>n;
	while(n--){
		ll x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		s[++cnt]=(node){x1,x2,y1,1};
		x[cnt]=x1;
		s[++cnt]=(node){x1,x2,y2,-1};
		x[cnt]=x2;
	}
	sort(s+1,s+cnt+1);
	sort(x+1,x+cnt+1);
	int num=unique(x+1,x+cnt+1)-(x+1);
	ll ans=0;
	for(int i=1;i<cnt;i++){
		int L,R;
		L=lower_bound(x+1,x+num+1,s[i].xl)-x;
		R=lower_bound(x+1,x+num+1,s[i].xr)-x;
		update(1,L,R,1,num,s[i].v);
		ans+=lenth[1]*(s[i+1].y-s[i].y);
	} 
	cout<<ans;
	return 0;
}
posted @ 2024-08-08 21:41  小惰惰  阅读(17)  评论(1编辑  收藏  举报
/* 鼠标点击求赞文字特效 */