扫描线学习笔记

感觉这个东西有点抽象啊,三道题下来也不是很懂的样子

前置知识

线段树(or树状数组),离散化

解决目标

矩形面积并,矩形周长并,二位数点覆盖等

矩形面积并

先看例题 矩形面积比

大意:给定n个矩形,求这n个矩形的并集覆盖的总面积

由于坐标值域一般极大,我们要考虑一种快速求面积交的方法,容易发现对于一个矩形并集的图像,我们可以将其分成若干个小矩形,对于每个小矩形,我们只需要知道他的长和宽就可以快速求出它的面积;

image

借用OIWIKI的图,我们记录每个初始矩形的上下两条边,以及其左右边界。从下往上扫描,若本次扫到下边界,就将其覆盖的左右区间内全部+1,反之则-1;

当然因为值域巨大,所以我们要先把左右边界离散化处理;因为两个相邻的左右边界内的操作是统一进行的,所以每两个相邻左右边界中的区间放到线段树上就是单位1的长度,这里为了方便存储,对于每条线段,我们都将它的右边界-1以方便操作

最终我们从下往上扫描每条线段并进行区间修改,统计时,只需要统计整棵线段树内值不为0的区间总长度(注意这里不是节点个数了,是放到图像上的实际区间长度)乘上距离上一个线段的纵向距离,就是本次的答案,最后累加即可;

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN=2e5+10;
struct node{int x,l,r;int tag;
}line[MAXN<<1];
int n,tot,b[MAXN<<1],cnt,tr_b[MAXN<<1];
map<int,int> mp;
bool cmp(node a,node b){return (a.x!=b.x?a.x<b.x:a.tag>b.tag);
}
struct tree{int sum,l,r,tag;
}tr[MAXN<<4];
#define ls id<<1
#define rs id<<1|1
void build(int id,int l,int r){tr[id].l=l,tr[id].r=r;tr[id].sum=tr[id].tag=0;if(l==r) return ;int mid=l+r>>1;build(ls,l,mid);build(rs,mid+1,r);return ;
}
void pushup(int id){if(!tr[id].tag){tr[id].sum=tr[ls].sum+tr[rs].sum;return ;}tr[id].sum=tr_b[tr[id].r+1]-tr_b[tr[id].l];return ;
}
void add(int id,int l,int r,int val){if(tr[id].l>r||tr[id].r<l) return ;if(tr[id].l>=l&&tr[id].r<=r){tr[id].tag+=val;if(tr[id].tag) tr[id].sum=tr_b[tr[id].r+1]-tr_b[tr[id].l];else pushup(id);return ;}add(ls,l,r,val);add(rs,l,r,val);pushup(id);return ;
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);cin>>n;for(int i=1,xf,yf,xs,ys;i<=n;i++){cin>>xf>>yf>>xs>>ys;line[++tot].x=xf;line[tot].l=yf,line[tot].r=ys;line[tot].tag=1;line[++tot].x=xs;line[tot].l=yf,line[tot].r=ys;line[tot].tag=-1;}n=tot,tot=0;for(int i=1;i<=n;i+=2){b[++tot]=line[i].l;b[++tot]=line[i].r;}sort(b+1,b+tot+1);for(int i=1;i<=tot;i++){if(!mp[b[i]]){mp[b[i]]=++cnt;tr_b[cnt]=b[i];}}for(int i=1;i<=n;i++){line[i].l=mp[line[i].l];line[i].r=mp[line[i].r];}sort(line+1,line+n+1,cmp);int ans=0;build(1,1,n);for(int i=1;i<=n;i++){
//		cout<<tr[1].sum<<" "<<line[i].x<<" "<<line[i-1].x<<"\n";if(i!=1) ans+=tr[1].sum*(line[i].x-line[i-1].x);
//		cout<<line[i].l<<" "<<line[i].r<<" "<<line[i].tag<<"\n"; add(1,line[i].l,line[i].r-1,line[i].tag);} cout<<ans;return 0;
} 

矩阵周长并

例题:矩形周长并

相对于面积并,这道题可能还要简单一点。容易发现对于两条相邻的线段,增加的周长长度就是其差的绝对值,只需要横着扫一遍,竖着再扫一遍即可;

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN=5e3+10,INF=1e4;
int n,trn,tot,b[MAXN<<2],tr_b[MAXN<<2],cnt;
int ans,lst_sum;
struct ng{int xf,yf,xs,ys;
}init[MAXN];
struct node{int x,tag,l,r;
}line[MAXN<<2];
bool cmp(node a,node b){return (a.x!=b.x?a.x<b.x:a.tag>b.tag);
}
map<int,int> mp;
struct tree{int l,r,sum,tag;//tag用来记录这段区间有没有被整体覆盖,如果被整体覆盖过直接求sum即可,否则从左右儿子获得信息 
}tr[MAXN<<4];
#define ls id<<1
#define rs id<<1|1
void build(int id,int l,int r){tr[id].l=l,tr[id].r=r;tr[id].sum=tr[id].tag=0;if(l==r) return ;int mid=l+r>>1;build(ls,l,mid);build(rs,mid+1,r);return ;
}
void pushup(int id){if(!tr[id].tag){tr[id].sum=tr[ls].sum+tr[rs].sum;return ;}tr[id].sum=tr_b[tr[id].r+1]-tr_b[tr[id].l];return ;
}
void add(int id,int l,int r,int val){if(tr[id].l>r||tr[id].r<l) return ;if(l<=tr[id].l&&tr[id].r<=r){tr[id].tag+=val;if(tr[id].tag) tr[id].sum=tr_b[tr[id].r+1]-tr_b[tr[id].l];else pushup(id);return ;}
//	int mid=tr[id].l+tr[id].r>>1;add(ls,l,r,val);add(rs,l,r,val);pushup(id);return ;
}
signed main(){ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);cin>>n;trn=n;for(int i=1;i<=n;i++){cin>>init[i].xf>>init[i].yf>>init[i].xs>>init[i].ys;init[i].xf+=INF,init[i].xs+=INF,init[i].yf+=INF,init[i].ys+=INF;}for(int i=1;i<=n;i++){line[++tot].x=init[i].xf;line[tot].l=init[i].yf,line[tot].r=init[i].ys;line[tot].tag=1;line[++tot].x=init[i].xs;line[tot].l=init[i].yf,line[tot].r=init[i].ys;line[tot].tag=-1;}n=tot,tot=0;for(int i=1;i<=n;i+=2){b[++tot]=line[i].l;b[++tot]=line[i].r;}sort(b+1,b+tot+1);for(int i=1;i<=tot;i++){if(!mp[b[i]]){mp[b[i]]=++cnt;tr_b[cnt]=b[i];}}for(int i=1;i<=n;i++){line[i].l=mp[line[i].l];line[i].r=mp[line[i].r];}sort(line+1,line+n+1,cmp);build(1,1,n);for(int i=1;i<=n;i++){
//		cout<<line[i].l<<" "<<line[i].r<<" ";add(1,line[i].l,line[i].r-1,line[i].tag);
//		cout<<lst_sum<<" "<<tr[1].sum<<"\n";ans+=abs(tr[1].sum-lst_sum);lst_sum=tr[1].sum;}tot=0,n=trn,cnt=0;mp.clear();for(int i=1;i<=n;i++){line[++tot].x=init[i].yf;line[tot].l=init[i].xf,line[tot].r=init[i].xs;line[tot].tag=1;line[++tot].x=init[i].ys;line[tot].l=init[i].xf,line[tot].r=init[i].xs;line[tot].tag=-1;}n=tot,tot=0;for(int i=1;i<=n;i+=2){b[++tot]=line[i].l;b[++tot]=line[i].r;}sort(b+1,b+tot+1);for(int i=1;i<=tot;i++){if(!mp[b[i]]){mp[b[i]]=++cnt;tr_b[cnt]=b[i];}}for(int i=1;i<=n;i++){line[i].l=mp[line[i].l];line[i].r=mp[line[i].r];}sort(line+1,line+n+1,cmp);build(1,1,n);for(int i=1;i<=n;i++){
//		cout<<line[i].l<<" "<<line[i].r<<" ";add(1,line[i].l,line[i].r-1,line[i].tag);
//		cout<<lst_sum<<" "<<tr[1].sum<<"\n";ans+=abs(tr[1].sum-lst_sum);lst_sum=tr[1].sum;}cout<<ans;return 0;
}

其实选定一个方向后,每次更新答案时同时加上两条高的贡献可以单次扫描解决这道题,但主播太懒了不想写(不想动脑

二维数点覆盖

例题:窗口的星星

我们发现对于二维平面内的散点并不好计算,但是我们可以把每个点扩充成一个w*h的矩形(因为题目中说了边界不算,所以实际要给坐标-1),对于这个抽象矩形内的所有点附上权值;

我们容易发现,只要两个点扩充的矩形有交,就代表可以用一个窗口将两个点同时框住;

那么事情就非常好办了,我们仍然采用传统扫描线的方法,在每次区间修改后找出整棵线段树上的最大值,也就是相交矩形权值和最大的部分,求max即可;

代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e6+10;
int T,n,w,h,cnt,b[MAXN<<1],tr_b[MAXN<<1],tot;
map<int,int> mp;
struct node{int x,y,l;
}e[MAXN];
struct le{int x,l,r,vl;
}line[MAXN<<1];
bool cmp(le x,le y){return (x.x!=y.x?x.x<y.x:x.vl>y.vl);
}
struct tree{int l,r,maxn,tag;
}tr[MAXN<<2];
#define ls id<<1
#define rs id<<1|1
void build(int id,int l,int r){tr[id].l=l,tr[id].r=r,tr[id].maxn=tr[id].tag=0;if(l==r) return ;int mid=l+r>>1;build(ls,l,mid);build(rs,mid+1,r);return ;
}
void pushup(int id){tr[id].maxn=max(tr[ls].maxn,tr[rs].maxn);return ;
}
void pushdown(int id){if(tr[id].tag){tr[ls].maxn+=tr[id].tag;tr[rs].maxn+=tr[id].tag;tr[ls].tag+=tr[id].tag;tr[rs].tag+=tr[id].tag;tr[id].tag=0;}return ;
}
void add(int id,int l,int r,int val){if(tr[id].l>r||tr[id].r<l) return ;if(l<=tr[id].l&&tr[id].r<=r){tr[id].maxn+=val;tr[id].tag+=val;return ;}pushdown(id);int mid=tr[id].l+tr[id].r>>1;if(l<=mid) add(ls,l,r,val);if(r>mid) add(rs,l,r,val);pushup(id);return ;
}
signed main(){ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);cin>>T;while(T--){cin>>n>>w>>h;if(w==1||h==1){cout<<"0\n";}for(int i=1;i<=n;i++){cin>>e[i].x>>e[i].y>>e[i].l;++cnt;line[cnt].x=e[i].x;line[cnt].l=e[i].y,line[cnt].r=e[i].y+h-1;line[cnt].vl=e[i].l;++cnt;line[cnt].x=e[i].x+w-1;line[cnt].l=e[i].y,line[cnt].r=e[i].y+h-1;line[cnt].vl=-1*e[i].l;}n=cnt,cnt=0;for(int i=1;i<=n;i+=2){b[++cnt]=line[i].l;b[++cnt]=line[i].r;}sort(b+1,b+n+1);for(int i=1;i<=cnt;i++){if(!mp[b[i]]){mp[b[i]]=++tot;tr_b[tot]=b[i];}}for(int i=1;i<=n;i++){line[i].l=mp[line[i].l];line[i].r=mp[line[i].r];}sort(line+1,line+n+1,cmp);build(1,1,tot);long long ans=0;for(int i=1;i<=n;i++){add(1,line[i].l,line[i].r,line[i].vl);ans=max(ans,tr[1].maxn);}cout<<ans<<"\n";mp.clear();cnt=tot=0;}return 0;
} 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汽车网站首页模板代码html网站架设

content: \20; 表示里面有值&#xff0c;你可以随便操作了

go-reids

初始化客户端 package mainimport ("context""fmt""github.com/redis/go-redis/v9" )var ctx = context.Background()func main() {rdb := redis.NewClient(&redis.Options{Addr: …

AI完美声音克隆及情绪控制,与真人无异,Lark下载介绍

在社交平台上,你是否刷到过一些魔性又神奇的视频?比如英文版《三国演义》、唐僧大战灭霸、川普说中文... 这些作品不仅完美复现了原角色的音色,连情感和韵律都做到了高度还原!更让人惊讶的是,它们居然全都是靠AI生…

WSL,适用于 Linux 的 Windows 子系统

WSL 是什么? WSL 是 Windows 系统的一项功能,允许在 Windows 上直接运行 Linux 发行版(如 Ubuntu、Debian、Kali 等),无需虚拟机或双系统。它能让开发者在 Windows 环境中无缝使用 Linux 工具、命令行和应用程序,…

学财税大信息应用,需要考CPA/税务师吗?

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

【CV】GAN代码解析: networks.py

【CV】GAN代码解析: networks.pyPosted on 2025-09-24 21:29 SaTsuki26681534 阅读(0) 评论(0) 收藏 举报import torch import torch.nn as nn from torch.nn import init import functools from torch.optim im…

怎么使用网站服务器网站建设中首页模板

文章目录 一&#xff0e;前言二&#xff0e;游戏预览1.启动2.开始游戏3.游戏结束4.排行榜 三&#xff0e;游戏思路四&#xff0e;总结 一&#xff0e;前言 第一次用PyQt做游戏&#xff0c;有点小紧张呢。本次使用PyQt5制作一款简单的打地鼠游戏&#xff0c;支持基本游戏玩法、…

厦门网站建设找哪家比较好昆明seo案例

在 Vue 中使用路由拦截器需要使用 Vue Router 提供的 beforeEach 方法。beforeEach 方法会在每个路由切换前&#xff0c;对路由进行拦截处理。可以在这个方法中进行一些验证或者权限认证&#xff0c;如果满足条件则继续跳转&#xff0c;否则取消跳转并进行相应处理。 下面是一…

9-24

在编译方式下,中间代码、代码优化是可以省略的 词法、语法、语义分析都是必须的并且不可以交换顺序 解释器参与运行控制,程序执行的速度慢符号表!!词法分析--输出记号流语法分析--输出语法树 语法分析主要是分析结…

APM v4.1.1 | 免费音乐听歌B站油管音乐播放器 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

代码随想录算法训练营第八天 |344.反转字符串、541. 反转字符串II、LCR 122. 路径加密

344.反转字符串 思路:双指针一个从后一个从前遍历,然后依次调换他们指向的元素。func reverseString(s []byte) {l := 0r := len(s) - 1for l < r{tmp := s[l]s[l] = s[r]s[r] = tmpr--l++} } 541. 反转字符串I…

9/24

离散课精力集中效果特别好,昨天没睡好今天没跑步,坐地铁回家洗了个澡

安装与卸载JDK8

卸载JDK8右键我的电脑>属性>搜索环境变量>删除JAVA_HOME 删除JDK所有文件 删除path下与java相关的目录 打开cmd 输入 java -version检查卸载是否成功安装JDK8搜索JDK8,找到下载地址下载电脑对应版本双击安装…

职友集 一家做职业点评的网站阳澄湖大闸蟹网站建设

Docker是一个开源的容器化平台&#xff0c;它允许开发者打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的Linux机器上&#xff0c;也可以实现虚拟化。以下是Docker的一些常用命令和环境部署的详解&#xff1a; Docker常用命令&#xff1a; 查看…

太仓手机网站建设常州电子商务网站建设

注释(Comments) 注释 Stylus支持三种注释&#xff0c;单行注释&#xff0c;多行注释&#xff0c;以及多行缓冲注释 单行注释&#xff1a; 跟JavaScript一样&#xff0c;双斜杠&#xff0c;CSS中不输出 多行注释&#xff1a; 多行注释看起来有点像CSS的常规注释。然而&a…

seo百度站长工具网站功能建设描述书

本文选自 《交易技术前沿》总第三十六期文章(2019年9月)陈靖宇深圳证券交易所 系统运行部Email: jingyuchenszse.cn摘要&#xff1a;为了应对基础设施规模不断上升&#xff0c;数据中心两地三中心带来的运维挑战&#xff0c;深交所结合现有基础设施现状&#xff0c;以通用性、灵…

网站开发者工具的网络选项大连模板网站制作公司

http://blog.csdn.net/sailor_8318/archive/2008/06/30/2599357.aspx【摘要】本文分析了内核的同步及互斥的几种机制&#xff1a;原子运算符(atomicoperator)、自旋锁Spinlock、等待队列Waitqueue、事件Event、completion、信号量Semaphore及其优化版互斥锁&#xff0c;详细分析…

个人网站 虚拟主机米绘花型设计师服务平台

雷军&#xff1a;共建一个更良性包容的汽车市场舆论环境 Figure 与 OpenAI 联手推出新机器人 亚马逊和 Google 悄悄降低对生成式 AI 的预期 小米生态链模式大改革&#xff0c;将进行分级管理 掌阅科技&#xff1a;致力打造国内首款真正 AI 阅读应用 荣耀称已投入 100 亿用于 AI…

深圳工信部网站备案信息查询中小企业 网站建设

题目描述 一个 NM 的由非负整数构成的数字矩阵&#xff0c;你需要在其中取出若干个数字&#xff0c;使得取出的任意两个数字不相邻&#xff08;若一个数字在另外一个数字相邻 8个格子中的一个即认为这两个数字相邻&#xff09;&#xff0c;求取出数字和最大是多少。 输入格式 第…

完整教程:【力扣LeetCode】 1413_逐步求和得到正数的最小值

完整教程:【力扣LeetCode】 1413_逐步求和得到正数的最小值2025-09-24 21:16 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !importa…