扫描线
时间:2019-09-28
本文章向大家介绍扫描线,主要包括扫描线使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
算法简介
扫描线算法用于求许多矩形的面积并。
算法流程
基本思想
对于一堆矩形,我们从下往上用一根水平的直线扫过去,把面积一块一块扫出来。从最下面一条矩形横边开始,每一块矩形面积,为直线扫到此处被矩形覆盖的长度乘以该直线到下一条横边的高度差。这个很好理解,看图说话:
思想就很简单易懂。
实现?
如何快速找扫描线长度?
我们把所有横边按照\(y\)排序,并且把它们的端点的\(x\)离散化,设离散化后\(i\)对应的原值为\(X[i]\)。
从下往上扫,一个矩形下面的称为入边,权值为\(1\),上面的为出边,权值为\(-1\)。
我们的线段树,每一个节点的左右为\(l、r\)的话,它维护的就是\(X[l]~X[r+1]\)这个扫描线上的区间(以保证每个节点维护的都是一个区间,且不会重叠)。每扫描到一条横边,如果是入边,线段树上对应节点\(+1\),如果是出边就\(-1\),这样该节点的数就代表扫描线对应的区间此时覆盖上的矩形数。如果不为0,该区间覆盖长度为区间长度,直接算就行;如果为0,说明该区间没有被完全覆盖,其被覆盖长度必须由其子节点更新(子节点没有完全覆盖就继续递归下去,叶子节点中间没有别的竖边,所以要么完全覆盖要么完全没被覆盖)。这样维护就搞定扫描线长度了。
令每个横边扫描线长度为\(len[i]\),则\(ans=\sum_{i=1}^{n-1} {len[i]*(y[i+1]-y[i])}\),大功告成。
代码
模板(洛谷P5490)
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,X[1000001],Y[1000001],cnt,ans;
struct Ju{
int l,r,y,f;
}a[1000001];
int a1,b1,a2,b2;
struct Tr{
int l,r,sum,v;
}tr[4000001];
inline int Read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
void Write(int x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9)
Write(x/10);
putchar(x%10+'0');
}
inline bool cmp(Ju a,Ju b)
{
return a.y<b.y;
}
void Build(int x,int l,int r)
{
tr[x].l=l,tr[x].r=r;
if(l==r)
return;
int mid=(l+r)>>1;
Build(x<<1,l,mid);
Build(x<<1|1,mid+1,r);
}
inline void Pushup(int x)
{
int l=tr[x].l,r=tr[x].r;
if(tr[x].sum)
tr[x].v=X[r+1]-X[l];
else
tr[x].v=tr[x<<1].v+tr[x<<1|1].v;//区间是否被覆盖没有可加性,例如两个子区间有覆盖,但没覆盖整个区间,那这个区间的值还是0
//如果两个区间都有覆盖,且覆盖了整个区间,这个区间的值却为0,怎么办?
//就通过pushdown来保证区间覆盖长度的正确性
}
void Add(int x,int l,int r,int k)
{
//if(l>=X[tr[x].r+1]||r<=X[tr[x].l])
// return;
if(l<=X[tr[x].l]&&r>=X[tr[x].r+1])
{
tr[x].sum+=k;
Pushup(x);
return;//每次查询总是从节点1开始查询,所以对于整个覆盖的区间,子区间不会被查询,就不修改了
}
int mid=(tr[x].l+tr[x].r)>>1;
if(l<=X[mid])
Add(x<<1,l,r,k);
if(r>X[mid+1])
Add(x<<1|1,l,r,k);
Pushup(x);//没覆盖整个区间就要修改子区间了
}
signed main()
{
n=Read();
for(register int i=1;i<=n;++i)
{
a1=Read(),b1=Read(),a2=Read(),b2=Read();
a[(i<<1)-1].y=b1,a[(i<<1)-1].l=a1,a[(i<<1)-1].r=a2,a[(i<<1)-1].f=1;
a[i<<1].y=b2,a[i<<1].l=a1,a[i<<1].r=a2,a[i<<1].f=-1;
X[(i<<1)-1]=a1,X[i<<1]=a2;
}
n=n<<1;
sort(X+1,X+n+1);
cnt=unique(X+1,X+n+1)-X-1;
sort(a+1,a+n+1,cmp);
Build(1,1,cnt-1);
for(register int i=1;i<=n;++i)
{
Add(1,a[i].l,a[i].r,a[i].f);
ans+=tr[1].v*(a[i+1].y-a[i].y);
}
Write(ans);
}
原文地址:https://www.cnblogs.com/SKTT1Faker/p/11600730.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- c++实例之通讯录管理系统之查找联系人功能(五)
- 【python-leetcode424-滑动窗口法】替换后的最长重复字符
- c++实例之通讯录管理系统之修改联系人功能(六)
- 解决arxiv.org打开pdf缓慢问题
- c++实例之通讯录管理系统之清空联系人功能(七)
- springboot任务之定时任务
- springboot检索之整合elasticsearch并使用jest操作
- springboot任务之邮件任务
- springboot安全之整合spring security实现(只有登录才有权限、不同用户显示不同内容、记住我)
- springboot任务之异步任务
- springboot缓存之@Cacheable中常用参数
- springboot开发之添加员工
- springboot分布式之整合zookeeper和dubbo
- springboot之热部署
- c++之内存模型