首页 > 其他分享 >扫描线

扫描线

时间:2024-10-29 20:43:53浏览次数:3  
标签:扫描线 线段 查询 答案 区间 las

之前写的搬一下


扫描线应该说是一种数据结构的维护思想,处理的问题经常是查询一段区间内子区间的信息,并且可以离线处理。

大致的流程是把询问离线下来,按某个东西排序,然后从 \(1\) 到 \(n\) 遍历序列,不断加上维护的信息,在某些时刻(比如到达一个询问的端点时)把对应询问的答案给搞出来,放到数组里面,接下一个询问。

刚才有个大佬回复说:扫描线本质上就是把一个维度轴当做时间轴来看,然后拿点 DS 去维护这个“时间轴”前进过程中的变化。这就很形象地解释了修改和查询一起进行的过程。这样我们边修改边查询,修改 \(n\) 次查询 \(m\) 次,复杂度 \(O((n+m)\log n)\)。

大概的代码框架,很简单:

for(int i=1,j=1;i<=n;++i){
		update();
		while(j<=m&&q[j].r==i){
			ans[q[j].id]=query();
			++j;
		}
	}

P9991 Ynoi Easy Round 2023 TEST_107

我们考虑一个极长子区间可能是长啥样的,有三种可能:

\(1.\) \([l′,r′]=[l,i]\),满足 \(a_{i+1}=x,\forall j\in[l,i],a_j\neq x\)

\(2.\) \([l′,r′]=[i,r]\),满足 \(a_{i-1}=x,\forall j\in[i,r],a_j\neq x\)

\(3.\) \(l′>l,r′<r\),满足 \(a_{l′-1}=a_{r′+1}=x,\forall j\in[l′,r′],a_j\neq x\)

先考虑 \(1\),我们记录 \(las_i\) 表示上一个 \(a_i\) 所在的位置。扫描线每扫过一个 \(i\) 就把线段树上 \(las_i\) 的位置改为 \(i\),这样你在 \(r_j=i\) 时线段树上 \([0,l_j-1]\) 的最大值就代表 \([l_j,r_j]\) 中那个符合 \(1\) 情况的 \(i\)。\(2\) 情况只需要将整个序列倒过来,更新 \(las_i\),同时把询问也倒过来,进行和 \(1\) 情况相同的操作就行。\(3\) 情况扫到 \(i\) 的更新就变成了把 \(las_i\) 位上变成 \((i-1)-(las_i+1)+1\),查询就直接查 \([l_j,r_j]\) 的最大值即可。

再说一下扫描线具体是怎么扫的:我们从 \(1\) 到 \(n\) 遍历原序列,每扫到一个 \(i\) 就 update,将线段树上 \(las_i\) 位置(一般化地来说就是和 \(i\) 形成有贡献的区间的一个位置)修改为 \(i\),表示在查询的区间包含 \(las_i\) 时 \([las_i,i]\) 这段区间是被包含的,可以产生贡献的。每扫到一个 \(r_j=i\) 时就进行查询,把答案更新到数组对应编号中,一次这样的扫描线是 \(O((n+m)\log n)\) 的。

for(int i=1,j=1;i<=n;++i){
		update(1,0,n,las[i],i); //注意这里会有-1,会涉及到0
		while(j<=m&&q[j].r==i){
			ans[q[j].id]=max(ans[q[j].id],query(1,0,n,0,q[j].l-1)-q[j].l);
			++j;
		}
	}

GSS2 - Can you answer these queries II

有参考价值的。显然不能像GSS1那样直接线段树维护前后缀最大子段和了,因为涉及到子区间,考虑能不能离线下来扫描线处理。这里有个trick:我们让线段树的每个叶节点表示以 \(i\) 为开头的最大子段和,每次在线段树上对 \([las_i+1,i]\) 加上 \(a_i\),这样就能保证相同的数的贡献覆盖区间无交,并维护区间历史最大值。维护历史最大值是很容易感性理解的,你还需要同时维护历史最大的 tag。

P9990 Ynoi Easy Round 2023 TEST_90

因为涉及到子区间个数计数,也就是需要考虑到所有子区间。一个trick是考虑扫描线记录线段树上的历史版本和,很好感性理解,因为我们扫描线是在不断加上新的元素的贡献,相当于将区间右端点的取值范围扩展到了当前的 \(i\),所以所有区间的答案之和就是所有历史版本之和。

然后考虑对每个数考虑他的贡献。显然他能影响到的区间和前几道题一样还是 \([las_i+1,i]\),而他的贡献相当于是将所有区间答案的奇偶性取反。想要统计答案还需要记录每个节点上答案为 \(0/1\) 的区间个数被统计的次数,记为 \(t0,t1\),这样每个区间的答案就是两个 tag 乘上对应答案的子区间个数。注意你每次修改都要对 \([1,n]\) 额外修改一次 t[1].t1++,t[1].ans+=t[1].sum1;,这样这个 \(t1\) 就可以下传到整颗线段树。

CF522D

扫描线板题,独立秒了欸。将 \(las[i]\) 修改成 \(i-las[i]\) 然后查区间最小值就做完了。

标签:扫描线,线段,查询,答案,区间,las
From: https://www.cnblogs.com/heshuwan/p/18514409

相关文章

  • 矩形面积并 - 扫描线模板
    扫描线模板(矩形面积并)首先离散化的思想,将各个线段细分到单位长,于是就是动态求当前值域内tag\(>1\)的数量。以下是参考代码,十分优美intn,cnt;llxx[N];structScanline{lly;lllx,rx;intinout;booloperator<(constScanline&t)const{......
  • P1502 窗口的星星(扫描线)
    关键在把矩形框点转化为点的影响放大为矩形,此时转变为求一个点的权值最大#include<bits/stdc++.h>usingnamespacestd;#definexfirst#defineysecondtypedefpair<int,int>PII;typedeflonglongll;typedefunsignedlonglongull;typedefunsignedintuint;type......
  • 扫描线-学习笔记
    扫描线-学习笔记引言:扫描线算法用于解决给出多个矩形组成的图形求解其面积、周长等问题。时间复杂度常见为\(O(n\log_2^n)\)级别,空间复杂度略大于\(O(n)\),属于线段树的一种运用。一、求面积题目:P5490【模板】扫描线&矩形面积并求\(n\)个四边平行于坐标轴的矩形的面积......
  • 扫描线
    include<bits/stdc++.h>usingnamespacestd;definepiipair<int,int>definemkpmake_pairdefinepbpush_backdefinemid((l+r)>>1)definels(x)(x<<1)definers(x)((x<<1)+1)defineLLlonglongdefineintlonglongconstin......
  • 扫描线总结
    引入面积并(周长并)如下图给你一堆矩形求它的面积并或周长并。显然直接做,就是考虑容斥,但明显不好做。那就思考如何切割或补,显然补完要减的图形也不规整,只能考虑割。如何将其割成规整的图形,明显矩形最容易计算和割。把它割成矩形后发现,每次遇到某个矩形的边就会变,所以考虑一条......
  • 扫描线
    引入扫描线一般运用在图形上面,它和它的字面意思十分相似,就是一条线在整个图上扫来扫去,它一般被用来解决图形面积,周长,以及二维数点等问题。Atlantis问题题意在二维坐标系上,给出多个矩形的左下以及右上坐标,求出所有矩形构成的图形的面积。解法根据图片可知总面积可以直接暴力......
  • 扫描线
    扫描线经典问题之求矩形面积并,可以使用线段树和扫描线。比方说我们要对这俩东西求面积并,我们简单分割一下。然后扫描线就是,从最下面一条绿线向上扫过去,遇到下底边则加上这个矩形,遇到上底边则减去这个矩形。回到这道题,发现给了我们矩形的两个角,那么上底边和下底边是好求的。......
  • 扫描线总结
    扫描线是线段树的典型应用。这玩意不难,用途也比较狭窄,重点在理解思想。例0【模板】扫描线题意求\(n\)个四边平行于坐标轴的矩形的面积并。对于\(100\%\)的数据,\(1\len\le{10}^5\),\(0\lex_1<x_2\le{10}^9\),\(0\ley_1<y_2\le{10}^9\)。解析如果用暴力......
  • 刍议线段树 3 (扫描线)
    扫描线扫描线是一种另外的思想,只是其中会运用到线段树以求优化。所以不要将扫描线简单的并为线段树的一个小拓展。例题:https://www.luogu.com.cn/problem/P5490大意:求\(n\)个四边平行于坐标轴的矩形的面积并。思路:纵向分割图形我们考虑把这些纵向矩形分割。那么,总面积就......
  • 扫描线
    Abstract介绍一下扫描线的经典用法。命名空间还挺好用的。A-扫描线(模板)Idea想象现在有一根线正在从左向右扫描,那么,我们就可以通过纵坐标上区间的覆盖情况去确定扫过的矩形覆盖的面积,区间覆盖情况可以用线段树去维护。实现细节见代码注释。Code#include<bits/stdc++.h>#......