首页 > 其他分享 >扫描线学习笔记

扫描线学习笔记

时间:2024-02-23 12:34:10浏览次数:28  
标签:lid int 线段 tr 笔记 学习 扫描线 id

1.引入

扫描线多用于图形上,是一条线在图形上扫来扫去,它一般被用来解决图形面积,周长,以及二维数点等问题。

2.扫描线求面积并

如下图:

我们模拟一条扫描线,使它从下往上扫过整个平面,这条扫描线会在遇到横向线段的时候停下来更新一些东西。那么整个图形就可以找出四条线段,如图:

更新的内容就是计算线段的长度,所以要记录线段左右端点的坐标,我们把坐标存入一个数组,记作x[],并将x排序,如图:

接着考虑,在扫描线单调向上时,如何判断哪里有面积,哪里是空的?

因为一个矩形有上下底,在往上扫的时候,总是先遇到下底,再遇到上底,所以我们可以给每条横向线段赋一个权值,例如下底为1,上底为-1,所以扫描线有权值的部分就是要计算的面积

然后考虑面积并的问题,显而易见,重叠部分只计算一次面积,两个矩形相交,一个矩形的横向边上至少有一个另一个矩形边上的点

如上图,X[1]和X[3]作为左右端点组成的线段可以分成两部分,X[1]和X[2]组成线段和X[2]和X[3]组成线段

所以,这个图形横向分成了4条边3部分,纵向分成了4条边3部分

扫描线从下往上扫,只能处理横向问题,纵向需要用线段树维护

可以用线段树的每一个节点储存一个线段,当然,这条线段不一定是一组左右端点截成的,如图,可以建一棵线段树:

当一条线段被扫到时,立即更新线段树每个节点维护的线段的覆盖长度和权值,例如扫到最下面的线段时,节点1,2维护的线段覆盖长度和权值就会被更新,所以不难看出,线段树维护的左右节点其实是线段的编号,另外维护线段覆盖长度和权值,这样扫描线扫有权值的部分就有我们要计算的面积,那么就更新线段覆盖长度

对于每一条线段,我们记录它的左右端点和纵坐标,权值,按照纵坐标优先升序排序,保证扫描线从下向上扫。

不难看出,x[]数组排序的意义是为了线段树更新覆盖长度时能直接减,另外,相同的X[]我们其实只需要一个就够了,所以我们要离散化处理,所以有:

s=∑线段覆盖长度*扫过的高度(即纵坐标的差)

例题:[poj1151]亚特兰蒂斯

https://gxyzoj.com/d/hzoj/p/3444

#include<cstdio>
#include<algorithm>
#define lid id<<1
#define rid id<<1|1
using namespace std;
int n,cnt;;
double s[20005];
struct node{
	double x,ay,by;
	int fl;
}p[20005];
bool cmp(node x,node y)
{
	return x.x<y.x;
}
struct seg_tree{
	double sum,l,r;
	int lazy;
}tr[80040];
void pushup(int id)
{
	if(tr[id].lazy>0)
	{
		tr[id].sum=tr[id].r-tr[id].l;
	}
	else
	{
		tr[id].sum=tr[lid].sum+tr[rid].sum;
	}
}
void build(int id,int l,int r)
{
	tr[id].lazy=0;
	tr[id].l=s[l],tr[id].r=s[r];
	if(r-l<=1)
	{
		tr[id].sum=0;
		return;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid,r);
	pushup(id);
}
void update(int id,double ay,double by,int fl)
{
	if(tr[id].l==ay&&tr[id].r==by)
	{
		tr[id].lazy+=fl;
		pushup(id);
		return;
	}
	if(tr[lid].r>ay) update(lid,ay,min(tr[lid].r,by),fl);
	if(tr[rid].l<by) update(rid,max(tr[rid].l,ay),by,fl);
	pushup(id);
}
int main()
{
	scanf("%d",&n);
	while(n!=0)
	{
		for(int i=1;i<=n;i++)
		{
			double ax,ay,bx,by;
			scanf("%lf%lf%lf%lf",&ax,&ay,&bx,&by);
			p[i]=(node){ax,ay,by,1};
			p[i+n]=(node){bx,ay,by,-1};
			s[i]=ay;
			s[i+n]=by;
		}
		sort(s+1,s+2*n+1);
		sort(p+1,p+2*n+1,cmp);
		build(1,1,2*n);
		update(1,p[1].ay,p[1].by,p[1].fl);
		double ans=0;
		for(int i=2;i<=2*n;i++)
		{
			ans+=(p[i].x-p[i-1].x)*tr[1].sum;
		//	printf("%.2lf %.2lf\n",tr[1].sum,ans);
			update(1,p[i].ay,p[i].by,p[i].fl);
		}
		printf("Test case #%d\nTotal explored area: %.2lf\n",++cnt,ans);
		scanf("%d",&n);
	}
	return 0;
}

3.扫描线求周长并

首先考虑纵边,画个图:

标红的是要计算的纵边长度,不难发现,由于两线之间距离固定,所以当前纵边的贡献为和上条线的距离\(\times\)当前边数,当前边数即当前不相交的线段数 ×2(也就是把相交的线段拼在一起后统计),所以,可以用线段树维护当前有多少条线段

记c为当前区间的线段条数,考虑转移,当sum不为0时,其为当前区间长度;否则要从左右儿子转移而来,直接相加肯定不对,因为有一些左右儿子的线段会相交

观察左右儿子的交界处(左儿子右端点、右儿子左端点),当且仅当它们均被线段覆盖时会相交,c需要减 1,所以再维护一个区间左右端点是否被覆盖的lc,rc 即可。sum为0时分别为左儿子lc,右儿子 rc,否则均为 1

接着考虑横边,与求面积并很像,也要枚举区间被覆盖的长度,画张图:

其中,红线为周长的贡献,绿线为区间被覆盖的长度,容易发现,红线就是两两绿线的差

例题: [IOI1998] [USACO5.5] 矩形周长

#include<cstdio>
#include<algorithm>
#include<cmath>
#define lid id<<1
#define rid id<<1|1
#define ll long long
using namespace std;
int n,x[10005];
ll ans;
struct node{
	int ax,bx,y,fl;
}a[10005];
bool cmp(node x,node y)
{
	if(x.y!=y.y) return x.y<y.y;
	return x.fl>y.fl;
}
struct tree{
	int sum,len,c;
	bool lc,rc;
}tr[80040];
void pushup(int id,int l,int r)
{
	if(tr[id].sum)
	{
		tr[id]=(tree){tr[id].sum,x[r+1]-x[l],1,1,1};
	}
	else
	{
		tr[id]=(tree){0,tr[lid].len+tr[rid].len,tr[lid].c+tr[rid].c,tr[lid].lc,tr[rid].rc};
		if(tr[lid].rc&&tr[rid].lc) tr[id].c--;
	}
}
void update(int id,int l,int r,int ql,int qr,int v)
{
	if(ql<=x[l]&&qr>=x[r+1])
	{
		tr[id].sum+=v;
		pushup(id,l,r);
		return;
	}
	int mid=(l+r)>>1;
	if(ql<x[mid+1]) update(lid,l,mid,ql,qr,v);
	if(qr>x[mid+1]) update(rid,mid+1,r,ql,qr,v);
	pushup(id,l,r);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int ax,ay,bx,by;
		scanf("%d%d%d%d",&ax,&ay,&bx,&by);
		a[i]=(node){ax,bx,ay,1};
		a[i+n]=(node){ax,bx,by,-1};
		x[i]=ax,x[i+n]=bx;
	}
	sort(a+1,a+2*n+1,cmp);
	sort(x+1,x+2*n+1);
	int m=unique(x+1,x+n*2+1)-x-1;
	int lst=0;
	//	printf("1");
	for(int i=1;i<2*n;i++)
	{
		update(1,1,m-1,a[i].ax,a[i].bx,a[i].fl);
		ans=ans+1ll*2*tr[1].c*(a[i+1].y-a[i].y)+1ll*abs(tr[1].len-lst);
		lst=tr[1].len;
	}
	printf("%lld",ans+a[n*2].bx-a[n*2].ax);
	return 0;
}

标签:lid,int,线段,tr,笔记,学习,扫描线,id
From: https://www.cnblogs.com/wangsiqi2010916/p/18029241

相关文章

  • 【文化课学习笔记】【数学】函数(上)
    【数学】函数(上)概念【本质】唯一确定的对应。【定义】一般地,设\(A,B\)是非空的实数集,如果对于集合\(A\)中的任意一个数\(x\),按照某种确定的对应关系\(f\),在集合\(B\)中都有唯一确定的数\(y\)和它对应,那么就称\(f:A\toB\)为从集合\(A\)到集合\(B\)的一个函数......
  • SpringMVC学习
    SpringMVC是Spring提供的用于简化web开发的框架。 1.5 Servlet能够响应请求的对象。接收请求,返回响应SpringMVC可以认为是Servlet的封装。  1.6SpringMVC开发流程回顾各种配置。Controller,DispatchServlet, 1.7......
  • 《程序是怎样跑起来的》第四章读书笔记
    内存IC中内存IC中有电源,地址信号,数据信号,控制信号等用于输入输出的大量引脚,通过为其指定地址,来进行数据的读写。像WR和RD这样可以让IC运行的信号称为控制信号。当WR和RD同时为0时,写入和读出的操作都无法进行。编程语言中的数据类型表示存储的是何种类型的数据。指针也是一种变量,......
  • 笛卡尔树学习笔记
    1.定义笛卡尔树是一种特殊的二叉树,同时满足小根堆和二叉搜索树的性质,笛卡尔树的每个节点有两个值(k,w),k满足二叉搜索树性质,w满足小根堆性质。Treap也是一种笛卡尔树2.性质如果k,w是分别两两不同的,那么笛卡尔树也是唯一的对于一颗笛卡尔树,它的中序遍历是原序列a在原序列中......
  • Syscall笔记
    本文首发:https://xz.aliyun.com/t/13687基础知识我们知道,系统核心态指的是R0,用户态指的是R3,系统代码在核心态下运行,用户代码在用户态下运行。系统中一共有四个权限级别,R1和R2运行设备驱动,R0到R3权限依次降低,R0和R3的权限分别为最高和最低。而我们的**syscall**是一个计算机......
  • rabbitmq交换机类型学习
    1Exchanges概念​RabbitMQ消息传递模型的核心思想是:生产者生产的消息从不会直接发送到队列。实际上,通常生产者甚至都不知道这些消息传递传递到了哪些队列中。​相反,生产者只能将消息发送到交换机(exchange),交换机工作的内容非常简单,一方面它接收来自生产者的消息,另一方面......
  • rabbitmq学习记录
    一、RabbitMQ的概念RabbitMQ是一个消息中间件:它接受并转发消息。你可以把它当做一个快递站点,当你要发送一个包裹时,你把你的包裹放到快递站,快递员最终会把你的快递送到收件人那里,按照这种逻辑RabbitMQ是一个快递站,一个快递员帮你传递快件。RabbitMQ与快递站的主要区别在于:它不......
  • 学习环境(浏览器与编辑器)配置,HTTP常识
    学习大纲一前端html5/css3写页面2.JaveScript/ES6/jQuery/Bootstrap写页面逻辑二PHP编程PHP语法,数据类型,面向对象,命令空间,数据库POD,Composer,MVC三Laravel框架微信小程序,CRM,Laravel基础,项目分析,数据表创建,前后台的完整开发流程,项目优化与项目上线学习......
  • 机器学习策略篇:详解为什么是ML策略?(Why ML Strategy?)
    为什么是ML策略?从一个启发性的例子开始讲,假设正在调试的猫分类器,经过一段时间的调整,系统达到了90%准确率,但对的应用程序来说还不够好。可能有很多想法去改善的系统,比如,可能想去收集更多的训练数据吧。或者会说,可能的训练集的多样性还不够,应该收集更多不同姿势的猫咪图片,或者更......
  • (17)Lazarus学习之StringGrid1
    01]下拉ComboBox1选择  参考:C:\lazarus\examples\gridexamples\gridcelleditorprocedureTForm1.StringGrid1SelectEditor(Sender:TObject;aCol,aRow:Integer;varEditor:TWinControl);beginif(aCol=3)and(aRow>0)thenbegin//哪些单元格显示Comb......