扫描线口胡

前言

本片博客写于 lxl 学长回校讲课后不久,是笔者的一篇学习笔记,在此会详细介绍扫描线的有关内容,知识点和题大部分是 lxl 的课件上的。如有勘误,欢迎指出!

前置

对信息的理解

对于维护的信息我们可以把与信息相关的每一种限制都看成平面上的一个维度,维度越高也就说明信息限制越多,对应的就越不好维护。通常作为维度的有时间、序列下标、数值等。

在一个 B 维直角坐标系下,第 i 维坐标在一个整数范围 [li,ri] 区间,内部所有信息就是 B 维正交范围。一维的正交范围称为区间,二维称为矩形,三维称长方体。

我们查询信息,实际就是选择一个 B 维正交范围,对于每一个维度查询的限制可能不相同。如果对一维没有限制,则这一维是平凡的可以降维;如果所有维度都有两条边的限制,则称这是一个 2B-sideB 维正交范围。

为延续 lxl 优秀的画图习惯,这里补一张图,下面一次是一到四 side 的矩形。

自由度体系

这个体系一般用于描述静态问题,我们根据询问的参数数量定义问题的自由度数。注意这里自由度的度数与维数不一定对应。一般将动态问题离线转化为静态问题会增加一自由度,也就是时间维。

扫描线和差分

实质

扫描线是将高自由度问题转化为动态低自由度问题的方式,扫描线扫不同的维,扫不同的方向,会将原静态问题转换为不同形式的动态问题。

一维扫描线

对于一个静态的二维问题,我们可以用扫描线扫一维,数据结构维护另一维。在扫的过程中我们会在数据结构上进行一些修改查询,如果查询可以差分就直接差分,因为差分在不对答案造成任何影响的情况下能够降低问题难度,所以能差分就无条件差分即可。

我们还可以换一个角度看扫描线,我们可以从序列的角度看待问题。我们的扫描线扫到的位置实际就是修改查询区间的右端点,我们维护一个数据结构,它支持对于当前的右端点 r,维护所有左端点 l 的信息。

关于扫描线扫一个多 side 的矩形,我们我们也许不好维护所有左端点的信息,但是能够很好维护前缀信息(也就是区间 [1,r] 的信息),这时我们会用差分。

差分方法

这里列举一点经典的差分方法:

  1. 对于序列上的区间 [l,r]:差分为 [1,r][1,l1] 的前缀。
  2. 对于树上的路径 (u,v):差分为 (rt,u)+(rt,v)(rt,lca)(rt,falca)
  3. 对于树上子树修改查询操作:用 dfs 序转化成区间问题。
  4. 对于树上子树修改、单点查询:可以转化成单点修改、前缀查询。

类似的转化还有很多,这些都需要我们有耐心步步转化,拆解问题。

差分的原因

根据 lxl 所讲,差分的问题就是“自由度”的问题。通过差分我们往往能将一个高自由度问题以极小代价转换为低维问题,而维数越低,信息越好处理。

也许有读者会问差分的常数,这一点不用担心。因为计算机做加减运算是比较快的,它真正慢的地方在于查询区间,你差分成前缀最多不到 log 个区间,而不差分可能出现接近 2log 个,所以差分的代价是很小的,你也可以自己手搓一点小数据试一试。

对于 4-side 矩形的扫描线

在可差分的前提下,我们可先将其差分成两个 3-side 的矩形。对于只有一条边的维度,我们可以用扫描线直接干掉,于是就把原问题变成一维动态问题了,这时候选择合适的数据结构维护即可。

基础题

A

题面

给一个长为 n 的序列,有 m 次查询,每次查区间 [l,r] 中值在 [x,y] 内的元素个数。

题解

这个问题叫二位数点,是一个非常经典但基础的扫描线,我们把序列和值域当成信息的两维,对于每次查询我们都相当于在平面内选择一个矩形,数矩形内点数。然后就按照上面讲的 4-side 矩形做法做,扫描线加上树状数组即可。

B

题面

给一个二维平面,上面有 n 个矩形,每个矩形坐标范围在 [1,n] 以内;有 m 次查询,每次查询给定一个二维平面上的点,求这个点被多少矩形包含。

题解

就是普通的扫面积、单点查。

过渡

接下来的内容与扫描线的运用有关,分为三种技巧,每种技巧笔者整理了一点经典的题。但不能保证题解写得很详细。

反演

这是一种正难则反的思想,对于正常查询操作不好做的题,我们可以考虑每个位置对答案的贡献。具体可以看后面的题。

数据结构

给一个长为 n 的序列,m 次查询:如果将区间 [l,r] 中所有数都 +1,那么整个序列有多少个不同的数?询问间独立。

要求:单 log

题解

我们首先可以考虑不同值对答案的贡献,因为不同的值贡献独立。但我们发现如果直接查区间外的和区间内的就会很麻烦,总之不好处理,于是就可以进行反演。

我们考虑每个位置对哪些询问有贡献,常见的处理方法是容斥一下,考虑每个位置对哪些询问没有贡献,这是相对好做的。于是我们考虑怎么样一个询问是满足条件的。

所以对于每个值 x 考虑什么样的查询区间会导致其无贡献。显然是原来是 x 的数全部被包含,且原来是 x1 的数全部没有被包含。这些限制实际上在通过预处理每个值的位置后变成区间左右端点。

所以现在考虑将区间的左右端点拿去建立平面直角坐标系,然后将满足条件的矩形加进去。因为矩形个数是 O(x-1出现次数) 级别的,所以一共有线性个矩形,然后就是扫描线随便搞搞结束,时间复杂度 O((n+m)logn)

窗口的星星

平面上有一些点,点有点权。给你固定长宽的矩形,求能覆盖的最大权值和。

数据范围1n104,0x,y231

题解

考虑如果扫描线去扫一维,那么第二维似乎无法处理,因为我们需要找到一段最值区间,这个不好维护。于是考虑每个点对矩形的一个顶点在那些位置有贡献,最后我们其实只需要找到全局最值点即可。

the soldier of love

n 个区间,有 m 次询问,每次询问给出一些点,求对于这些点而言,有多少个初始给定的区间包含了至少这些点中的一个点。

要求:单 log

题解

因为区间可能包含若干点,不好统计,所以考虑容斥。我们还是用反演的思路,我们去找相邻两点之间的矩形个数。所以就把初始矩形抽象成一个点,查询就是查线性个区间。

区间子区间

这种题一般是查询一个区间内所有子区间的信息,一般的方法是把一个单独的区间看成平面上的点,查询的区间就是一个上三角。对于这种图形有不同的处理方式,因为平面右下半边贡献为零,所以可以把上三角补成矩形,且通常是 2-side 矩形。

可以扫描线扫一维。于是我们需要做的就是区间维护、查询区间历史和。那么现在考虑如何用线段树维护历史和?

历史和线段树

我们需要维护两个序列 AB,其中 A 代表当前序列,B 代表每个位置维护的历史和。考虑每次操作是修改 A,每次查询是查询 B,所以我们需要用一些东西将 AB 联系起来。考虑我们在每个时刻都会将 A 中的信息累加到 B 中,所以现在需要对 B 维护一种特殊的标记,这种标记是全局累加每个位置对应的 A 的信息。

有了特殊的标记后我们就要思考如何合并并且下放标记?比如现在依次有修改操作 +a、历史合并 b 次、修改操作 +c 和历史合并 d 次这四种标记,我们应该以何种方式合并呢?首先明确一点,对于历史合并的标记,可以当成先修改再合并,也可以将合并往后挪一个单位时间变成先合并再修改,这是不影响的。这里讲后者。

现在当成有历史合并 a 次、修改操作 +b、历史合并 c 次、修改操作 +d。如果我们把合并的操作放一起 会怎么样?现在就变成了历史合并 a+c 次、修改操作 +b+d。但是这肯定还不对,因为“修改操作 +b”对“历史合并 c 次”造成了影响!考虑到每次合并都会少一个额外的值 W,于是我们就对于一系列操作打包维护,也就是维护一个三元组 (a,b,W)。合并三元组的时候修改操作就直接累加;对于 B 序列需要加上之前的损失值;对于 W 不仅需要加上原来的额外值,还有这一次的损失值 W=bc。至此标记的合并下放也就结束了,其他地方就照搬普通线段树即可。其实对于历史和线段树还有一些更优秀的理解,也就是把每个节点当成矩阵,但在此不赘述,只放一个学长写的博客

一点题

放一个板子-->this

当然这个题可以用 st 表薄纱区间和线段树。

CF526F

给定一个 n×n 的棋盘,其中有 n 个棋子,每行每列恰好有一个棋子。对于所有的 1kn,求有多少个 k×k 的子棋盘中恰好有 k 个棋子,输出其总和。

题解

考虑如何刻画充要条件?对于一个区间 [l,r],考虑第二维有两个限制,并且每一列一定都有点,所以我们需要让区间内最高点刚好是 r,且最低点刚好是 l。于是就可以写出一个式子:maxmin=rl。现在考虑如何求满足此条件的区间数。

对于一个等式我们先移项变成 maxminr+l=0,然后考虑到原来的左式一定不小于右式,所以相当于现在我们在平面中进行 4n 次修改操作后看权值为零的点个数。

具体的,对于 +lr 的操作我们可以发现它对应了一行或一列,于是有 2n 次直线的修改。对于 +maxmin 的操作我们可以通过最值分值找到每个值对应的矩形,然后就可以直接维护啦。

补:其实对于最后的解决方式还可以用单调栈啊,也许码量还会减少。

实际写代码的时候会发现对于一操作(根据写法不同也可能是二操作)我们不好维护,这时候其实可以在初始化里面解决,具体怎么办可以自行思考。(这是提升代码能力的好机会)

CF997E

升级一下上一道题。现在还有区间查询。

题解

其实就是套上一个历史和线段树就做完了。

ABC248Ex

稍微改一下前面的题。现在要求 [l,r] 中满足 maxminrl+k 的数量。

数据范围0k3

题解

lxl 瑞平:糖体。

的确如此,因为 k 很小所以就是 CF526F 直接做,然后多维护最小值加 1~3 即可。

NOIP2022比赛

唉都2025了啊

题目给了两个序列和若干询问,每次询问一个区间 [l,r],求 i=lrj=lr(maxi=lrAi)(maxi=lrBi)

题解

考虑这还是板子的加强版呐!但是一道很典的题。可以套路地想到历史和线段树,现在考虑维护哪些信息?

首先我们会维护区间长、历史和、还有答案。因为我们可以将最大值通过单调栈变成区间加,所以只用考虑加矩形的时候会发生什么变化?我们考虑以下形式:

(A+x)(B+y)

这个形式可拆成:

AB+Ay+Bx+xy

于是我们就可以维护 A×BAB 即可。

TEST_90(个人觉得比较有意思的题)

求区间子区间满足内部出现颜色数为奇数的个数。

题解

容易想到用异或操作来翻译此题。现在只用考虑如何维护标记。对于依次有形如若干异或标记、若干历史和标记、若干异或标记、若干历史和标记的东西,我们其实可以把一段异或化简成一次异或操作。如果两段历史和中间没有标记了就不管它。现在我们稍微分析一下中间有一个异或标记的情况。

假设前面有 a 次历史和,后面有 b 次历史和,我们一次异或会将所有 01 翻转,所以所有的 0 有 a 历史和贡献,所有的 1 有 b 次。所以我们维护 0 和 1 的个数并且分开维护历史贡献和即可。

换维扫描线

顾名思义,就是如果维护一维后剩下一维不好处理就可以考虑换维。常见的比如时间-序列平面中我们通常都是扫时间,但是如果遇到有的神秘题目不好做的时候,我们可以尝试扫序列维护时间。反正就是哪一位好差分就扫哪一位。

然后就看一点题目找感觉吧(

序列

支持区间加、查询点单值小于某数的时间。

题解

第一种操作相当于一个 3-side 的矩形,查询就是一条线。如果我们扫时间维,会发现这条线不好处理,但是如果换一维我们查询线就相当于查区间 rank。这种东西用分块可以维护。

因为笔者个人原因,不想写题所以这部分的内容就咕了。主要是这种题跟普通扫描线区分度不高,可能是一种个人习惯,有时又需要一点意识。

一点杂题

即便看不到未来

查询静态区间中长度为一到十的极长值域连续段个数。

题解

可以考虑扫描线。若我们扫区间右端点,那么我们需要维护每一个左端点的答案。首先思考现在新加入一个数 x 后会有什么影响?

假设 x 上一次出现的位置是 lasx,那么对于左端点在 [1,lasx] 的区间都没有影响。又考虑到我们只需要维护十以内的极长段,所以就对不同的长度都维护一个数据结构。并且加入一个数只会影响相邻的值域,所以我们可以把 [x11,x+11] 中最后出现的位置拿出来暴力求解。

首先解释为什么需要拿出值域在 [x11,x+11] 中的数。对于值域在 [x9,x+9] 中的数,它们会和 x 产生贡献,所以肯定要;但是考虑如果 x 成为一段的开头或结尾时,这一段可能会长度大于十,这时我们需要判断一下这一段的长度,所以要往后看两位。如果看一位你只能知道他是否大于等于十而不是十一。

然后就是考虑从右往左更新答案。如果枚举到的值 y 在包含 x 连续段的开头或结尾,从当前位置开始到后面某一段贡献都会加一;如果枚举的 y 将两小段连在一起,就停止两小段的区间加,开始新的连续段的区间修改。最后单点查。所以可以差分一下用树状数组维护。时间复杂度 O((n+m)klogn)

代码很短就不放了。

铃原露露

给一棵有根树和排列 A,静态查询区间中满足 x,y,{Ax,Ay}[l,r],Alca(x,y)[l,r] 的区间个数。

题解

对于这种区间子区间题,我们需要对平面维护若干矩形修改和矩形查询。现在可以先考虑什么样的 (x,y) 满足条件,我们尝试用一些特殊的条件去刻画题目要求。

首先我们要思考我们要统计什么?我们可以对于每一个三元组 (x,y,lca) 统计信息,可以找出每一个三元组满足条件的区间然后算并。但是这样非常麻烦!所以我们正难则反,找出不满足条件的区间然后找不包含任何矩形的点。

现在思考对于一个二元组 (x,y),x<y,其不合法区间范围。我们应该分类讨论 lca 的范围。

z=lca(x,y),首先对于 zx,y 之间的情况我们不用管,因为不存在区间(特指 lr 的区间)不合法。

然后另外两种情况可以见下图。

现在唯一的问题是矩形数量太多,我们需要扔掉一些被完全覆盖的区间,这个问题用树上启发式合并枚举就可以解决。具体的,固定一个 lca,对一个点 ulca 的其他子树中找 u 的前驱后继,这样每个点可以找到 O(log) 个。找前驱后继就用平衡树维护即可。时间复杂度是 O(nlog2n)

posted @   Lyrella  阅读(21)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示