扫描线

一.问题引入

扫描线
n 个边平行于坐标轴的四边形的面积并。

二.算法思路

我们将图分成若干段(下图每一段的面积已用彩笔标出),就像拿一根竖直的线从左往右扫一遍,如图:

面积并就等于这些段的面积和。
那每一段的面积怎么求呢?

hi 表示第 i 段在 y 轴上的长度(可能不连续)。
例:x1 x2 中间橙色的那一段 h1 就等于橙色小方块的高度;x2 x3 中间黄色的那一段 h2 就等于两个黄色小方块的高度和。
然后每一段的面积就等于 (xi+1xi)×hi

怎么求 xi
按每个大四边形的左端排序就好了。

怎么求 hi
这里我们就需要用线段树了。
先讲一下思路:我们按照 y 轴建线段树,令每个四边形的左端+1,右端-1(左端的意思是左边的那条边覆盖的区间,右端同理),如图:

为什么要这样做呢?
这样我们就可以在每一段知道 y 轴上的每个位置被几个四边形覆盖(因为+1,-1是成对出现的,一个大四边形覆盖一个区间,从左端一直覆盖到右端,左端+1表示这段开始被该四边形覆盖,直到右端-1表示不再被该四边形覆盖)。
当前段 hi 就等于此时 y 轴上所有大于0的区间长度和。

相当于有两种操作:
1.区间加;
2.求整个区间所有权值大于0的区间长度和。
于是我们就可以利用线段树进行维护了。

线段树维护信息:
1.cnt 当前整个区间覆盖次数。
2.len 不考虑祖先节点的前提下,cnt>0 的区间总长。

三.代码

这份代码是横着切的。

点击查看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define lson root<<1
#define rson root<<1|1
using namespace std;
inline int read(){
	int w=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-1') f=-1;
		ch=getchar(); 
	}
	while(ch>='0'&&ch<='9'){
		w=w*10+ch-'0';
		ch=getchar();
	}
	return w*f;
}
const int N=1000000;
int n; 
int X[N*2+5]; 
long long ans;
struct CSGO{
	int l,r,h,val;
}ran[N*2+5];
bool cmp(CSGO a,CSGO b){
	return a.h<b.h; 
}
struct tree{
	int l;
	int r;
	int sum;
	int len;
}t[N*4+5];
void build(int root,int l,int r){
	t[root].l=l,t[root].r=r;
	t[root].sum=t[root].len=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(lson,l,mid),build(rson,mid+1,r);
}
void pushup(int root){
	int l=t[root].l,r=t[root].r;
	if(t[root].sum) t[root].len=X[r+1]-X[l];
	else t[root].len=t[lson].len+t[rson].len; 
}
void change(int root,int l,int r,int k){
	if(X[t[root].r+1]<=l||r<=X[t[root].l]) return;
	if(l<=X[t[root].l]&&X[t[root].r+1]<=r){
		t[root].sum+=k;
		pushup(root);
		return;
	}
	change(lson,l,r,k);
	change(rson,l,r,k);
	pushup(root);
}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		int x1=read(),y1=read(),x2=read(),y2=read();
		ran[i*2-1].l=x1,ran[i*2-1].r=x2,ran[i*2-1].h=y1,ran[i*2-1].val=1;
		ran[i*2].l=x1,ran[i*2].r=x2,ran[i*2].h=y2,ran[i*2].val=-1;
		X[i*2-1]=x1,X[i*2]=x2;
	}
	n*=2;
	sort(ran+1,ran+1+n,cmp);
	sort(X+1,X+1+n);
	int cur=unique(X+1,X+n+1)-X-1;
	build(1,1,cur-1);
	for(int i=1;i<=n;i++){
		change(1,ran[i].l,ran[i].r,ran[i].val);
		ans+=1ll*t[1].len*(ran[i+1].h-ran[i].h);
	}
	cout<<ans;
	return 0;
	

}





 
posted @   Travller  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示