Scanline
Introduction
Scanline is generally applied in graphics. As its name suggests, it involves a line sweeping across the entire graph. It is commonly used to solve problems such as area of shapes, perimeter, and two-dimensional point counting.
2D Rectangle Area Union Problem
In a 2D coordinate system, given the lower-left and upper-right coordinates of multiple rectangles, find the total area formed by all rectangles.
Process
As shown in the image, the total area can be directly calculated by brute force. But what if the data is large? This is where the scanline algorithm comes in.
Now suppose we have a line sweeping from bottom to top:
As shown, the entire rectangle is divided into small rectangles of different colors. The height of each small rectangle is the distance swept, while the horizontal width keeps changing.
Mark each rectangle's top and bottom edges: mark the bottom edge as 1 and the top edge as -1. Whenever a horizontal edge is encountered, add the mark of this edge to the weight of the interval (its projection on the horizontal axis).
Note
This operation is similar to traversing a bracket sequence: an opening bracket adds 1, a closing bracket subtracts 1. The "weight" corresponds to the current depth, and whether the "weight" is greater than 0 corresponds to whether we are currently inside brackets, i.e., whether this interval should be counted in the width of the small rectangle.
The width of the small rectangle(s) (there may be more than one) is the total length of intervals on the number axis where the weight is greater than 0.
Implementation
Use a segment tree to maintain the length of the rectangle, i.e., the points on the entire number axis where the coverage count is greater than 0. The requirements are as follows:
- Add 1 or subtract 1 to the weight of an interval.
- Count the total "interval length sum" on the entire number axis where the interval weight is greater than 0.
If you try to implement this directly using a standard segment tree template, you might encounter some difficulties. Specifically, during interval addition, even if the modification interval overlaps with the node's managed interval, we cannot know in constant time how the coverage count changes. This is because we cannot directly know: within the managed range, how long an interval changes from 1 to 0 (or from 0 to 1).
This problem can be implemented with a simple divide-and-conquer approach: maintain two pieces of information for each node's managed interval: "the number of times the interval is completely covered v[]" (similar to a lazy tag that is not pushed down) and "the covered length w[]".
Discretization is required.
Luogu P5490【Template】Scanline & Rectangle Area Union Reference Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
「POJ 1151」Atlantis Reference Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | |
Exercises
- 「POJ1177」Picture
- 「POJ3832」Posters
- Luogu P1856 [IOI1998] [USACO5.5] Rectangle Perimeter Picture
- The contribution of horizontal edges is the change in covered length.
- Calculating in two directions separately avoids discussion of vertical edges.
- When sorting operations, consider the case where two rectangle edges coincide.
- The data range allows simulation in quadratic time without using a segment tree.
B-Dimensional Orthogonal Range
A B-dimensional orthogonal range refers to a set of points within a B-dimensional Cartesian coordinate system where the \(i\)-th coordinate lies in an integer range \([l_i,r_i]\).
Generally, a one-dimensional orthogonal range is called an interval, a two-dimensional orthogonal range is called a rectangle, and a three-dimensional orthogonal range is called a cuboid (what we often call 2D point counting is a 2D orthogonal range).
For a static 2D problem, we can use a scanline to sweep one dimension and a data structure to maintain the other dimension. As the scanline sweeps from left to right, it generates some modifications and queries on the dimension maintained by the data structure. If the queried information is differentiable, use difference directly; otherwise, use divide and conquer. Differences are usually maintained with a Fenwick tree or segment tree, but because Fenwick trees are easier to write and have smaller constants, most people choose to use a Fenwick tree. Divide and conquer usually refers to CDQ divide and conquer (but that is not covered here).
Another easier way to view the problem is from the sequence perspective rather than the 2D plane perspective. From this angle, the scanline essentially enumerates the right endpoint \(r=1\cdots n\), maintaining a data structure that supports queries: for the current \(r\), given a value \(l\), what is the answer from \(l\) to \(r\). That is, the scanline sweeps the query right endpoints, and the data structure maintains answers for all left endpoints, or traverses one dimension while the data structure maintains the other.
Complexity is generally \(O((n+m)\log n)\).
2D Point Counting
Given a sequence of length \(n\), there are \(m\) queries. Each query asks for the number of elements in the interval \([l,r]\) whose values are in \([x,y]\).
This problem is called 2D point counting. We can see it is equivalent to querying the number of points inside a rectangle on a 2D plane. Here we describe the simplest approach: scanline + Fenwick tree.
Clearly, this is a static 2D problem. We can convert it into a dynamic 1D problem via scanline. To maintain the dynamic 1D problem, we use a data structure to maintain the sequence; here we can use a Fenwick tree.
First discretize all queries. Use a Fenwick tree to maintain the values. For each query's \(l\) and \(r\), when enumerating to \(l-1\), count the current number of values in \([x,y]\), denoted \(a\). Continue enumerating; when reaching \(r\), count the current number of values in \([x,y]\), denoted \(b\). Then \(b-a\) is the answer for that query.
Example Problems
Luogu P2163 [SHOI2007] Gardener's Troubles
First discretize. Let \(ans_{x, y}\) be the number of points contained in a rectangle with lower-left corner \((0, 0)\) and upper-right corner \((x, y)\). Then the answer to a query can be expressed via difference as \(ans_{c, d} - ans_{a - 1, d} - ans_{c, b - 1} + ans_{a - 1, b - 1}\).
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | |
Luogu P1908 Reverse Pairs
Indeed, reverse pairs can also be solved using scanline thinking. Consider transforming the problem of counting reverse pairs into: for each position \(i\) from back to front, count the number of points in the interval \([i+1,n]\) whose values are in \([0,a_i]\). The data range in the problem is \(10^9\), so discretization is necessary. We can traverse the array from back to front, updating the Fenwick tree (segment tree) each time we encounter a number, then count how many numbers are less than the current enumerated number. Since we traverse from back to front, the number of values smaller than the current one is exactly the number of reverse pairs for that element. This can be done with point update and range query on a Fenwick tree or segment tree.
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | |
Luogu P1972 [SDOI2009] HH's Necklace
Brief description: Given a sequence, answer multiple queries: how many distinct numbers are in the interval \([l,r]\).
For such problems, we can derive properties and then use scanline to enumerate all right endpoints, with a data structure maintaining the answer for each left endpoint. We can also transform the problem onto a 2D plane, turning it into a rectangle query problem.
In this problem, let \(pre_i\) be the previous occurrence position of \(a_i\) in the sequence. If \(a_i\) has not appeared before, set \(pre_i = 0\). According to the problem, if a number appears multiple times in an interval, it only contributes once. We can assume that the contribution of each number comes from its first occurrence position in the interval. Then it can be seen that the total contribution equals the number of positions where \(pre_x \le l - 1\). This can be proven by contradiction.
Now the problem becomes: Given a sequence \(pre\), answer multiple queries: how many \(pre_i \le l - 1\) are in the interval \([l,r]\).
We can treat \(pre_i\) as a point on a 2D plane: \(i\) is the x-coordinate, \(pre_i\) is the y-coordinate. The problem transforms into a 2D point counting problem: each query asks for the number of points in the rectangle with lower-left corner \((l,0)\) and upper-right corner \((r,l - 1)\).
Note that this query is differentiable. We can split the query into the difference between the rectangle with lower-left \((0,0)\) and upper-right \((r,l - 1)\) and the rectangle with lower-left \((0,0)\) and upper-right \((l - 1,l - 1)\). This facilitates using scanline.
Single operation complexity is \(O(\log n)\). There are \(n\) point addition operations and \(2m\) query operations, so total time complexity is \(O((n + m) \log n)\).
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | |
Exercises
- Luogu P8593「KDOI-02」A Bullet's Throw Application of reverse pairs.
- AcWing 4709. Triple Weaker version of the above, also an application of reverse pairs.
- Luogu P8773 [Blue Bridge Cup 2022 Provincial A] Select Number XOR Modified version of HH's Necklace.
- Luogu P8844 [Chuanzhi Cup #4 Preliminary] Little Card and Fallen Leaves Transform tree problem into sequence problem and then perform 2D point counting.
In summary, the main idea of 2D point counting is to maintain one dimension with a data structure while enumerating the other dimension.
References
- cnblogs/Yang1208: Scanline Explanation, Dynamic Opening Segment Tree
- csdn/riba2534: POJ1151 Atlantis Solution
- csdn/刀刀狗 0102: POJ1151 Atlantis Solution
- A Brief Discussion on Scanline
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用