扫描线总结

news/2024/10/9 12:25:02

引入

面积并(周长并)
如下图给你一堆矩形求它的面积并或周长并。
image
显然直接做,就是考虑容斥,但明显不好做。
那就思考如何切割或补,显然补完要减的图形也不规整,只能考虑割。
如何将其割成规整的图形,明显矩形最容易计算和割。
把它割成矩形后发现,每次遇到某个矩形的边就会变,所以考虑一条线从下向上扫,每遇到一个与扫描线平行的边就计算一次答案。
面积并答案为线上被覆盖的长度乘与上一条边的距离。
周长并答案分俩个部分,平行的就是线上被覆盖的长度,垂直的就是线上有多少个端点(在线段内的不算)乘与上一条边的距离。
如何快速维护线上的覆盖长度和有多少段点,显然一个区间修改问题,用线段树。

代码实现

周长并

#include <bits/stdc++.h>
using namespace std;
const int N=5e4+10;
int n,ans,cnt,mx=-100010,mi=100010,las=0;
struct node
{int h,x,y,w;
}bi[N*2];
bool cmp(node x,node y)
{if(x.h==y.h)return x.w>y.w;//先加后减,不让会错
/*
2
0 0 4 4
0 4 4 8
*/return x.h<y.h;
}
struct stu
{int sum;//整个区间被整体覆盖了几次(类似lazytag,但不下传)int num;//整个区间被几条互不相交的线段覆盖int len;//整个区间被覆盖的总长度bool lflag,rflag;
}sh[N*4];
void pushup(int x,int l,int r)
{if(sh[x].sum){sh[x].num=1;sh[x].len=r-l+1;sh[x].lflag=sh[x].rflag=1;}else if(l==r){sh[x].num=0;sh[x].len=0;sh[x].lflag=sh[x].rflag=0;}else {sh[x].num=sh[x<<1].num+sh[x<<1|1].num;if(sh[x<<1].rflag&&sh[x<<1|1].lflag)sh[x].num--;sh[x].len=sh[x<<1].len+sh[x<<1|1].len;sh[x].lflag=sh[x<<1].lflag;sh[x].rflag=sh[x<<1|1].rflag;}
}
void modify(int x,int l,int r,int lt,int rt,int w)
{if(lt<=l&&rt>=r){sh[x].sum+=w;pushup(x,l,r);return ;}int mid=(l+r)>>1;if(lt<=mid)modify(x<<1,l,mid,lt,rt,w);if(rt>mid)modify(x<<1|1,mid+1,r,lt,rt,w);pushup(x,l,r);return;
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++){int x1,y1,x2,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2);bi[++cnt].h=y1,bi[cnt].w=1,bi[cnt].x=x1,bi[cnt].y=x2;bi[++cnt].h=y2,bi[cnt].w=-1,bi[cnt].x=x1,bi[cnt].y=x2;mx=max(mx,x2);mi=min(mi,x1);}if(mi<0){for(int i=1;i<=cnt;i++){bi[i].x+=-mi+1;bi[i].y+=-mi+1;//加一与mx相匹配}mx-=mi;}sort(bi+1,bi+cnt+1,cmp);for(int i=1;i<=cnt;i++){modify(1,mi,mx,bi[i].x,bi[i].y-1,bi[i].w);//减一是因为这是长度为1的叶子的右端点,叶子的编号是左端点即右端点-1.while(bi[i+1].h==bi[i].h&&bi[i+1].w==bi[i].w){i++;modify(1,mi,mx,bi[i].x,bi[i].y-1,bi[i].w);}//同一类型一起处理ans+=abs(sh[1].len-las);//覆盖长度的变化las=sh[1].len;ans+=sh[1].num*2*(bi[i+1].h-bi[i].h);//垂直的边的长度}printf("%d",ans);return 0;
}

面积并

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
#define int long long
int n,ans,xx[N],cnt,mx=1e9+7,mi=0,las=0;
struct node
{int h,x,y,w;
}bi[N*2];
bool cmp(node x,node y)
{return x.h<y.h;
}
struct stu
{int sum;//整个区间被整体覆盖了几次(类似lazytag,但不下传)int len;//整个区间被覆盖的总长度
}sh[N*4];
void pushup(int x,int l,int r)
{if(sh[x].sum)sh[x].len=xx[r+1]-xx[l];else if(l==r)sh[x].len=0;else sh[x].len=sh[x<<1].len+sh[x<<1|1].len;//除了覆盖长度外都不要了
}
void modify(int x,int l,int r,int lt,int rt,int w)
{if(lt<=l&&rt>=r){sh[x].sum+=w;pushup(x,l,r);return ;}int mid=(l+r)>>1;if(lt<=mid)modify(x<<1,l,mid,lt,rt,w);if(rt>mid)modify(x<<1|1,mid+1,r,lt,rt,w);pushup(x,l,r);return;
}
signed main()
{scanf("%lld",&n);for(int i=1;i<=n;i++){int x1,y1,x2,y2;scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);if(x1>x2)swap(x1,x2);if(y1>y2)swap(y1,y2);bi[++cnt].h=y1,bi[cnt].w=1,bi[cnt].x=x1,bi[cnt].y=x2;bi[++cnt].h=y2,bi[cnt].w=-1,bi[cnt].x=x1,bi[cnt].y=x2;mx=max(mx,bi[cnt].y);mi=min(mi,bi[cnt].x);xx[i]=bi[cnt].x;xx[i+n]=bi[cnt].y;}sort(xx+1,xx+2*n+1);int tot=unique(xx+1,xx+2*n+1)-xx-2;sort(bi+1,bi+cnt+1,cmp);for(int i=1;i<=cnt;i++){int x=lower_bound(xx+1,xx+tot+2,bi[i].x)-xx;int y=lower_bound(xx+1,xx+tot+2,bi[i].y)-xx-1;//离散化了modify(1,1,tot,x,y,bi[i].w);ans+=sh[1].len*(bi[i+1].h-bi[i].h);}printf("%lld",ans);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/54030.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

folium地图绘制库和报错解决

1.资源库导入pip install folium -i https://pypi.tuna.tsinghua.edu.cn/simple 2.使用时报错解决 2.1 导入 使用报错 2.2 问题解决

Java异常详解(全文干货)

介绍Throwable Throwable 是 Java 语言中所有错误与异常的超类。 Throwable 包含两个子类:Error(错误)和 Exception(异常),它们通常用于指示发生了异常情况。 Throwable 包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等…

《2024 年最新 YouTube 转 MP3 攻略

在当今数字化时代,我们常常会遇到想要将 YouTube 上的精彩视频内容转换为 MP3 音频格式以便于随时随地收听的情况。以下为大家介绍几种最新的实用方法: **方法一:利用在线工具** youtubemp3dl- **youtubemp3dl**:特别适用于 Windows 和 Mac 操作系统,是一款出色的基于互联…

主观与客观,破除DDD凭经验魔咒

本文书接上回《学习真DDD的最佳路径》,关注公众号(老肖想当外语大佬)获取信息:最新文章更新;DDD框架源码(.NET、Java双平台);加群畅聊,建模分析、技术实现交流;视频和直播在B站。神秘的“凭经验” 一千个人眼中有一千个哈姆雷特,每个人的经历不同,认知不同,那么看…

【Linux网络编程】Reactor模式与Proactor模式

【Linux网络编程】Reactor模式与Proactor模式 Reactor模式 Reactor 模式是指主线程即 IO 处理单元只负责监听文件描述符上是否有事件发生,有则立刻将该事件通知给工作线程即逻辑单元,除此之外,主线程不做任何其它实质性的动作。读写数据,接受新的连接,以及处理客户请求均在…

2024, 是时候告别CentOS了

到了2024年, 不管你有多喜欢CentOS, 也到了该告别CentOS的时候了. 那个可能在你职业生涯中陪伴了你非常多年, 一直稳定运行的Linux系统, 在2024年后, 已经不再是你可靠的选择了. 最后一个仍然还在维护中的CentOS 7将于2024年6月底就END OF LIFE了. 这意味着, 如果你仍然继续使用…

StringBuilder和StringBuffer的区别

一 区别 StringBuilder 线程不安全 StringBuffer 线程安全,原因是它的主要方法用了syncronized关键字修饰二 分析 看源码见