扫描线复习

扫描线复习

题单:https://www.luogu.com.cn/training/1098270#problems

扫描线是一种解决二维数点问题的有力工具,常用线段树实现。离线查询问题经常需要转为二维数点,这时就要用到扫描线。在这类问题中,若关乎某数出现的次数,一般需要在值域上构建链表。另外,一些计算几何问题也常用扫描线解决。

P1856 [IOI 1998 / USACO5.5] 矩形周长 Picture

考虑什么操作会对矩形周长造成影响。不难想出,扫描线上的连通块个数 * 2 就是区间上下边界的条数。线段树维护区间加、区间内连通块个数即可。不太好写,需要想想上传操作。

P1908 逆序对

统计 \(a_i > a_j \land i < j\) 的数对个数。考虑先固定一维,显然要考虑固定 \(i<j\),即从后往前加数,然后扫描线扫一下即可。

P6278 [USACO20OPEN] Haircut G

值域不大,手动模拟一下可以发现,逆序对的贡献可以挂到值上。考虑每次算出答案的增量,然后做前缀和即可。

P10814 【模板】离线二维数点

把所有操作都离线下来,然后处理这个区间的左右边界所算的贡献(一负一正),然后纳入该次询问的答案即可。

P1972 [SDOI2009] HH 的项链

颜色数 \(10^6\),可以开数组记录信息。

我们注意到,每种颜色在序列中出现的位置从前往后构成一个链表。可记 \(pre_i\) 表示 \(i\) 位置的颜色上一次出现的位置,因此区间 \([l,r]\) 的贡献为 \(\sum\limits_{i=l}^r[pre_i < l]\)(请自行理解)。然后转成二维数点。

P4113 [HEOI2012] 采花

延续上一题的思路,你发现一个数被纳入贡献的等价条件是它上上次出现的位置小于 \(l\)。然后在我们移动左端点的时候,左端点所在数字上上次出现的位置变为了上次出现的位置,所以需要维护链表某节点的前一项和前两项。

需要注意,在移动左端点的时候,需要清除树状数组上旧的信息。

P1502 窗口的星星

一个点能被矩形覆盖到的区域可抽象为以它为左下端点的一个矩形。于是每个矩形都有一个权值,维护区间加、区间最大值线段树,扫描线即可。

P8734 [蓝桥杯 2020 国 A] 奇偶覆盖

显然需要分别计算奇点和偶点的个数。类比传统扫描线,一次覆盖操作相当于区间奇偶性反转。因此需要维护支持区间加、区间奇数 / 大偶数的个数的线段树。不建议用 0/1 + 异或模拟,因为需要特判某偶数是否为 0,很麻烦。

代码由于未知原因 MLE,现在调不出来。

P3582 [POI 2015 R1] 影迷 Movie-goer

经典题了。最大子段和显然能用线段树维护。考虑怎么把重复元素解决掉。

我们从 \(1\) 到 \(n\) 遍历,每次遍历到一个新位置的时候都更新答案。当遍历到的位置 \(i\) 满足其与之前的某个位置 \(j\) 满足 \(f_i = f_j\),可以考虑将 \(i\) 位置的贡献不变,将 \(j\) 位置的贡献变为 \(-f_j\)(请自行思考原因)。如果 \(j\) 之前仍存在位置与其重复,直接将该位置的贡献置为 \(0\),保证算非法子段的时候,能抵消成 \(0\) 的贡献。套路地,需要维护某位置的 \(f\) 值上次出现的位置与上上次出现的位置,仍构成链表。

从以上几道题可以总结,关乎区间内某数出现次数的扫描线问题,一般需要在值域上维护链表。

P9284 [AGM 2023 资格赛] 海盗

这种东西容易想到转化成区间最值。发现船 SUNK 的等价条件是船所覆盖区间最小值大于 \(0\),船 SUNK 的等价条件是区间最小值等于 \(0\) 且区间 \(0\) 的个数等于区间长度,其余情况船 HIT。然后对于横船和竖船分别跑一遍扫描线即可。

代码非常难写!

P9478 [NOI2023] 方格染色

95 pts:可以把斜线暴力拆成若干个单点,是扫描线板子。

100 pts:横线和竖线直接扫描线。注意到斜线个数极少,因此考虑暴力处理出斜线的面积并,以及斜线与横线、竖线和其他斜线的交点,最后把斜线覆盖到的面积计算出来即可。实现方面可以用 Hash 或者 map<pair<int, int>, bool>,推荐后者。

不好写。

P5524 [Ynoi2012] NOIP2015 充满了希望

考虑没有 1 操作怎么做。

注意到序列中某值只会受到它前面的离它最近的那次修改的影响。因此对于序列中的每个位置,记录上次覆盖它的操作索引,并记录该索引对应的修改值。于是需要一种支持区间改单点查的数据结构,使用线段树。

对于每次单点询问操作,考察上次覆盖询问位置的操作索引 \(pre\) 与这次询问的操作索引 \(cur\)。设当前包含该操作的询问区间为 \([l,r]\),显然必须保证 \(l \le pre \lt cur \le r\) 才能产生贡献,转为二维数点问题。

因此把每个操作 3 离线下来,挂在询问点上;再把所有询问区间离线下来,挂在右端点上,这样能保证在右端点单调递增时 \(cur \le r\) 恒成立。每次新加入一个右端点时,载入该右端点的所有操作 3。开一个树状数组,将 \(val[cur]\) 拍到树状数组的 \(pre\) 位置,只需考察所有满足 \(pre \ge l\) 的 \(pre\) 产生的贡献即可。

考虑操作 1,本质上是交换两数的 \(pre\)。至此本题做完。

P6940 [ICPC 2017 WF] Visual Python++

没有匹配方案,先考虑贪心匹配。感性理解,应当让每个右下角匹配与其最近的左上角(关键字任意),实现可以用 set 和 lower_bound。

考虑一种匹配方案不合法的情况,即仍存在两矩形相交。如下图所示:

image-20261013191518158

这种情况下,矩形 \(ABCD\) 包含了点 \(E\)。因此可以想到维护一根从左往右扫的扫描线,当遇到边 \(AB\) 时在两端点处进行单点加;同理,遇到边 \(EF\) 时在 \(E,F\) 处执行单点加。此时遇到边 \(CD\),先将单点贡献减去,然后查询区间 \(CD\) 的和,发现存在多余的点 \(E\) 的贡献未被清除,即表明存在相交矩形。与此同时,内含或相离的矩形则不会出现这种情况,请自行思考原因。

于是,维护支持单点加、区间求和的数据结构,使用树状数组。

非常难写,至今未通过本题。

P4137 Rmq Problem / mex

一个自然数在区间出现过,当且仅当该自然数在全局首次出现的位置属于这个区间。

考虑离线询问,固定左端点。当左端点右移时,原左端点新的“首次出现的位置”即为它以原左端点为起的下一次出现的位置。因此仍然在值域上维护链表。

线段树维护某数 \(i\) 首次出现的位置 \(fst[i]\),查询一个区间 \([l,r]\) 的 \(\mathrm{mex}\) 等价于查询值域上第一个满足 \(fst[i] > r\) 的 \(i\)。可以线段树上二分。

posted @ 2026-10-13 19:32  L-Coding  阅读(6)  评论(0)    收藏  举报