Пересечение прямоугольника с множеством непересекающихся отрезков (segment tree)
| НЕТ ВОЙНЕ |
|
24 февраля 2022 года российское руководство во главе с Владимиром Путиным развязало агрессивную войну против Украины. В глазах всего мира это военное преступление совершено от лица всей страны, всех россиян. Будучи гражданами Российской Федерации, мы против своей воли оказались ответственными за нарушение международного права, военное вторжение и массовую гибель людей. Чудовищность совершенного преступления не оставляет возможности промолчать или ограничиться пассивным несогласием. Мы убеждены в абсолютной ценности человеческой жизни, в незыблемости прав и свобод личности. Режим Путина — угроза этим ценностям. Наша задача — обьединить все силы для сопротивления ей. Эту войну начали не россияне, а обезумевший диктатор. И наш гражданский долг — сделать всё, чтобы её остановить. Антивоенный комитет России |
| Распространяйте правду о текущих событиях, оберегайте от пропаганды своих друзей и близких. Изменение общественного восприятия войны - ключ к её завершению. |
| meduza.io, Популярная политика, Новая газета, zona.media, Майкл Наки. |
Задача
Есть множество непересекающихся отрезков в , нужно уметь отвечать на запросы, какие из них пересекают границу данного axis-aligned (стороны параллельны осям координат) прямоугольника.
Дерево отрезков
Для решения задачи потребуется структура данных, которая называется дерево отрезков (segment tree). NB: по-русски этими словами принято обозначать несколько другую структуру данных, здесь не о ней.
Пусть — множество отрезков на вещественной оси. Возьмем концы этих отрезков и отсортируем их, получим точки . Назовем множеством элементарных интервалов .
Построим сбалансированное бинарное дерево, листам которого будут соответствовать элементарные интервалы, а внутренним вершинам — объединения интервалов в потомках. Будем обозначать интервал, соответствующий вершине , . В каждой вершине будем хранить соответствующий ей интервал и множество отрезков , таких что и . По-человечески это значит, что интервал вершины должен полностью лежать внутри отрезка и для родителя вершины это не так. По картинке должно быть понятно.
| Лемма (Оценка на память): |
Дерево отрезков занимает памяти. |
| Доказательство: |
|
Всего элементарных интервалов не больше (в случае, когда все отрезки не пересекаются: для каждого отрезок левее , , , и еще один ). Так как дерево сбалансированное, его глубина . Утверждается, что один отрезок на одной глубине встречается не более двух раз. Пусть это не так, возьмем три упорядоченные вершины , содержащие этот отрезок. Очевидно, что сосед тоже содержит этот отрезок (см. картинку). Тогда этот отрезок должен содержаться в . Противоречие. Глубина , всего отрезков , на каждом уровне отрезок встречается раз — победа. |
Построение дерева
Сначала сортируем концы отрезков из за . За собираем сбалансированное дерево (просто поднимаясь от листьев к корню и объединяя интервалы). Осталось найти для вершин дерева. Для этого вставим туда каждый отрезок по такому алгоритму:
def InsertSegment(v, [x : x']): if : v.Add([x : x']) else: if : InsertSegment(v.Left, [x : x']) if : InsertSegment(v.Right, [x : x'])
Это работает за , потому что на каждом уровне есть не более двух вершин, в которые отрезок нужно вставить и еще не более двух вершин, содержащих его концы. Таким образом, на каждом уровне нужно обработать не более четырех вершин. Итого для всех отрезков получаем .
Запрос
Теперь научимся отвечать на запрос, какие отрезки содержат заданную точку.
def Query(v, p, S): S = S I(v) if !v.IsLeaf: if : Query(v.Left, p, S) else Query(v.Right, p, S) Query(root, p, )
Запрос работает за , где — размер ответа.
Алгоритм
Чтобы решить исходную задачу, нужно уметь находить, какие отрезки из пересекают axis-aligned отрезок . Рассмотрим случай, когда параллелен оси , для все делается аналогично.
В дереве отрезков будем хранить их проекции на ось . Это позволит найти, какие отрезки пересекают бесконечную вертикальную линию. Осталось найти, какие из них пересекают заданный отрезок.
Используем то, что отрезки не пересекаются, и : это значит, что отрезки внутри элементарного интервала могут быть вертикально упорядочены. В вместо списка теперь будем хранить дерево поиска.
При запросе будем выдавать только те отрезки, для которых верхняя граница запроса выше точки пересечения с вертикальной линией, а нижняя — ниже (то есть все, что находится между lower_bound и upper_bound в дереве, проверяем выше/ниже поворотом).
Таким образом, обработка одной вершины занимает времени, где — количество отрезков в ответе, полученных из вершины , всего посещенных вершин , итого . Препроцессинг выполняется за из-за вставки в дерево поиска.
Ссылки
- Английская википедия
- de Berg, van Kreveld, Overmars, Schwarzkopf "Computational Geometry Algorithms and Applications", p. 231
