Сжатие координат дерево отрезков

Сжатие координат дерево отрезков

pakhomovee → Codeforces Round #893 (Div. 2) Editorial

K evin114514 → I’m Kevin114514, AMA!

itsvarsharma → Hello colours.

Error_Yuan → About #893 C

abcsumits → Help me to find number of solution of this equation efficiently

Некропост

MikeMirzayanov → Изменение правил об использовании стороннего кода в соревнованиях Codeforces

T heQueenOfHatred → 2023 KSAAC Summer · solved.ac Arena #4 Announcement

Thaumic_Executor → Should CodeForces make the time of the contests more diverse?

Byakko → Invitation to 2023 USP Try-outs

Ibrahim-Elsayed → How to solve this problem?

JanBobi → How to block users in Codeforces

Некропост

Rezwan.Arefin01 → Checklist for OI Problems (WebApp)

Некропост

MikeMirzayanov → Codeforces Beta Round #9

Некропост

awoo → Educational Codeforces Round 56 [рейтинговый для Div. 2]

lumibons → CEOI 2023

mohit_jx64 → [Invitation] Coding Events — Esya’23 IIIT-Delhi

Некропост

MikeMirzayanov → Codeforces Beta Round #7

Некропост

removed1 → Разбор задач Codeforces Beta Round #1

Некропост

RAD → Codeforces Beta Round #22 (Див. 2)

Некропост

e-maxx → Codeforces Beta Round #30. Разбор A

PinkieRabbit → AI language models and cheating in online contests

induk_v_tsiane → Codeforces Round #892 (Div. 2) Editorial

steveonalex → [Tutorial] Divide and Conquer Offline Query — A Niche Way to solve Static Range Query

Некропост

RAD → Codeforces Round #113 (Div. 2) Разбор задач

MikeMirzayanov → Filter for Past Contests

Источник

Сжатое многомерное дерево отрезков

Вообще говоря, с поставленной задачей справится и обычное [math]p[/math] -мерное дерево отрезков. Для этого достаточно на [math]i[/math] -том уровне вложенности строить дерево отрезков по всевозможным [math]i[/math] -тым координатам точек множества [math]A[/math] , а при запросе использовать на каждом уровне бинарный поиск для установления желаемого подотрезка. Очевидно, запрос будет делаться за [math]O(\log^p\,n)[/math] времени, а сама структура данных будет занимать [math]O(n^p)[/math] памяти.

Оптимизация

Для уменьшения количества занимаемой памяти можно провести оптимизацию [math]p[/math] -мерного дерева отрезков. Для начала, будем использовать дерево отрезков с сохранением всего подотрезка в каждой вершине. Другими словами, в каждой вершине дерева отрезков мы будем хранить не только какую-то сжатую информацию об этом подотрезке, но и все элементы множества [math]A[/math] , лежащие в этом подотрезке. На первый взгляд, это только увеличит объем структуры, но не все так просто. При построении будем действовать следующим образом — каждый раз дерево отрезков внутри вершины будем строить не по всем элементам множества [math]A[/math] , а только по сохраненному в этой вершине подотрезку. Действительно, незачем строить дерево по всем элементам, когда элементы вне подотрезка уже были «исключены» и заведомо лежат вне желаемого [math]p[/math] -мерного прямоугольника. Такое «усеченное» многомерное дерево отрезков называется сжатым (англ. compressed).

Читайте также:  Розмарин масло розовое дерево

Построение дерева

Рассмотрим алгоритм построения сжатого дерева отрезков на примере множества [math]A[/math] , состоящего из [math]4[/math] -х взвешенных точек в [math]2[/math] -мерном пространстве (плоскости):

  • Cоставим массив из всех [math]n[/math] элементов множества [math]A[/math] , упорядочим его по первой координате, построим на нём дерево отрезков с сохранением подмассива в каждой вершине
    Tree built.png
  • Все подмассивы в вершинах получившегося дерева отрезков упорядочим по следующей координате
    Sorted y.png
  • Повторим построение дерева для каждого из них (координата последняя, поэтому в вершинах этих деревьев мы уже ничего строить не будем — подмассивы в каждой вершине можно не сохранять)
    Tree completed.png

Псевдокод

buildSubarrayTree(element[] array): // построение одномерного дерева отрезков на массиве array с сохранением подмассива в каждой вершине buildNormalTree(element[] array): // построение обычного одномерного дерева отрезков на массиве array getInsideArray(vertex v): // получение подмассива, сохраненного в вершине vertex buildCompressedTree(element[] array, int coordinate = 1): // рекурсивная процедура построения сжатого дерева отрезков if coordinate < p sort(array, coordinate) // сортировка массива по нужной координате segmentTree = buildSubarrayTree(array); foreach v: vertex in segmentTree buildCompressedTree(getInsideArray(v), coordinate + 1); if coordinate == p sort(array, coordinate) buildNormalTree(array);

Анализ полученной структуры

Легко понять, что сжатое [math]p[/math] -мерное дерево отрезков будет занимать [math]O(n\log^\,n)[/math] памяти: превращение обычного дерева в дерево с сохранением всего подотрезка в каждой вершине будет увеличивать его размер в [math]O(\log\,n)[/math] раз, а сделать это нужно будет [math]p-1[/math] раз. Но расплатой станет невозможность делать произвольный запрос модификации: в самом деле, если появится новый элемент, то это приведёт к тому, что мы должны будем в каком-либо дереве отрезков по второй или более координате добавить новый элемент в середину, что эффективно сделать невозможно. Что касается запроса веса, он будет полностью аналогичен запросу в обычном [math]p[/math] -мерном дереве отрезков за [math]O(\log^p\,n)[/math] .

См. также

Источники информации

Источник

Дерево отрезков

Дерево отрезков — очень мощная и гибкая структура данных, позволяющая быстро отвечать на самые разные запросы на отрезках.

Читайте также:  Фисташки дерево где растет

Рассмотрим конкретную задачу: дан массив $a$ из $n$ целых чисел, и требуется отвечать на запросы двух типов:

  1. Изменить значение в ячейке (т. е. реагировать на присвоение a[k] = x ).
  2. Вывести сумму элементов $a_i$ на отрезке с $l$ по $r$.

Оба запроса нужно обрабатывать за время $O(\log n)$.

Структура дерева отрезков

Чтобы решить задачу, сделаем с исходным массивом следующие манипуляции.

Посчитаем сумму всего массива и где-нибудь запишем. Потом разделим его пополам, посчитаем сумму на половинах и тоже где-нибудь запишем. Каждую половину потом разделим пополам ещё раз, и так далее, пока не придём к отрезкам длины 1.

Эту последовательность разбиений можно представить в виде дерева.

Корень этого дерева соответствует отрезку $[0, n)$, а каждая вершина (не считая листьев) имеет ровно двух сыновей, которые тоже соответствуют каким-то отрезкам. Отсюда и название — «дерево отрезков».

Разные полезные свойства

Высота дерева отрезков равна $\Theta(\log n)$: на каждом новом уровне длина отрезка уменьшается вдвое. Этот факт будет ключевым для оценки асимптотики операций.

Любой полуинтервал разбивается на $O(\log n)$ неперекрывающихся полуинтервалов, соответствующих в вершинам дерева: с каждого уровня нам достаточно не более двух отрезков.

Дерево содержит менее $2n$ вершин: первый уровень дерева отрезков содержит одну вершину (корень), второй уровень — в худшем случае две вершины, на третьем уровне в худшем случае будет четыре вершины, и так далее, пока число вершин не достигнет $n$. Таким образом, число вершин в худшем случае оценивается суммой $n + \frac + \frac + \frac + \ldots + 1 < 2n$. Значит, оно линейное по памяти.

При $n$, отличных от степеней двойки, не все уровни дерева отрезков будут полностью заполнены. Например, при $n=3$ левый сын корня есть отрезок $[0, 2)$, имеющий двух потомков, в то время как правый сын корня — отрезок $[2, 3)$, являющийся листом.

Ок, как это нам поможет?

Опишем, как с помощью такой структуры решить исходную задачу.

Запрос обновления. Нам нужно обновить значения в вершинах таким образом, чтобы они соответствовали новому значению $a[k] = x$.

Изменим все вершины, в суммах которых участвует $k$-тый элемент. Их будет $\Theta(\log n)$ — по одной с каждого уровня.

Это можно реализовать как рекурсивную функцию: ей передаётся текущая вершина дерева отрезков, и функция выполняет рекурсивный вызов от одного из двух своих сыновей (от того, который содержит $k$-ый элемент в своём отрезке), а после этого — пересчитывает значение суммы в текущей вершине точно таким же образом, как мы это делали при построении дерева отрезков.

Читайте также:  Майнкрафт мод рубка дерево

Запрос суммы. Мы знаем, что во всех вершинах лежат корректные значения, и нам с помощью них посчитать сумму на отрезке.

Сделаем тоже рекурсивную функцию, рассмотрев три случая:

  1. Если отрезок вершины лежит целиком в отрезке запроса, то вернуть записанную в ней сумму.
  2. Если отрезки вершины и запроса не пересекаются, то вернуть 0.
  3. Иначе разделиться рекурсивно на 2 половины и вернуть сумму этой функции от обоих детей.

Чтобы разобраться, почему это работает за $O(\log n)$, нужно оценить количество «интересных» отрезков — тех, которые порождают новые вызовы рекурсии. Это будут только те, которые содержат границу запросов — остальные сразу завершатся. Обе границы отрезка содержатся в $O(\log n)$ отрезках, а значит и итоговая асимптотика будет такая же.

Дерево отрезков можно использовать для гораздо большего, чем только для суммы.

Далее этой главе мы рассмотрим разные реализации и варианты этой структуры и их применения.

Источник

Сжатие координат дерево отрезков

pakhomovee → Codeforces Round #893 (Div. 2) Editorial

K evin114514 → I’m Kevin114514, AMA!

itsvarsharma → Hello colours.

Error_Yuan → About #893 C

abcsumits → Help me to find number of solution of this equation efficiently

Некропост

MikeMirzayanov → Изменение правил об использовании стороннего кода в соревнованиях Codeforces

T heQueenOfHatred → 2023 KSAAC Summer · solved.ac Arena #4 Announcement

Thaumic_Executor → Should CodeForces make the time of the contests more diverse?

Byakko → Invitation to 2023 USP Try-outs

Ibrahim-Elsayed → How to solve this problem?

JanBobi → How to block users in Codeforces

Некропост

Rezwan.Arefin01 → Checklist for OI Problems (WebApp)

Некропост

MikeMirzayanov → Codeforces Beta Round #9

Некропост

awoo → Educational Codeforces Round 56 [рейтинговый для Div. 2]

lumibons → CEOI 2023

mohit_jx64 → [Invitation] Coding Events — Esya’23 IIIT-Delhi

Некропост

MikeMirzayanov → Codeforces Beta Round #7

Некропост

removed1 → Разбор задач Codeforces Beta Round #1

Некропост

RAD → Codeforces Beta Round #22 (Див. 2)

Некропост

e-maxx → Codeforces Beta Round #30. Разбор A

PinkieRabbit → AI language models and cheating in online contests

induk_v_tsiane → Codeforces Round #892 (Div. 2) Editorial

steveonalex → [Tutorial] Divide and Conquer Offline Query — A Niche Way to solve Static Range Query

Некропост

RAD → Codeforces Round #113 (Div. 2) Разбор задач

MikeMirzayanov → Filter for Past Contests

Источник

Оцените статью