MAXimal | |
добавлено: 11 Jun 2008 10:54 Содержание [скрыть] Sqrt-декомпозицияSqrt-декомпозиция — это метод, или структура данных, которая позволяет выполнять некоторые типичные операции (суммирование элементов подмассива, нахождение минимума/максимума и т.д.) за Сначала мы опишем структуру данных для одного из простейших применений этой идеи, затем покажем, как обобщать её для решения некоторых других задач, и, наконец, рассмотрим несколько иное применение этой идеи: разбиение входных запросов на sqrt-блоки. Структура данных на основе sqrt-декомпозицииПоставим задачу. Дан массив ОписаниеОсновная идея sqrt-декомпозиции заключается в том, что сделаем следующий предпосчёт: разделим массив Можно считать, что длина одного блока и количество блоков равны одному и тому же числу — корню из тогда массив Хотя последний блок может содержать меньше, чем Таким образом, для каждого блока Итак, пусть эти значения Иллюстрация (здесь через На этом рисунке видно, что для того чтобы посчитать сумму в отрезке (примечание: эта формула неверна, когда Тем самым мы экононим значительное количество операций. Действительно, размер каждого из "хвостов", очевидно, не превосходит длины блока РеализацияПриведём сначала простейшую реализацию: // входные данные int n; vector<int> a (n); // предпосчёт int len = (int) sqrt (n + .0) + 1; // и размер блока, и количество блоков vector<int> b (len); for (int i=0; i<n; ++i) b[i / len] += a[i]; // ответ на запросы for (;;) { int l, r; // считываем входные данные - очередной запрос int sum = 0; for (int i=l; i<=r; ) if (i % len == 0 && i + len - 1 <= r) { // если i указывает на начало блока, целиком лежащего в [l;r] sum += b[i / len]; i += len; } else { sum += a[i]; ++i; } } Недостатком этой реализации является то, что в ней неоправданно много операций деления (которые, как известно, выполняются значительно медленнее других операций). Вместо этого можно посчитать номера блоков int sum = 0; int c_l = l / len, c_r = r / len; if (c_l == c_r) for (int i=l; i<=r; ++i) sum += a[i]; else { for (int i=l, end=(c_l+1)*len-1; i<=end; ++i) sum += a[i]; for (int i=c_l+1; i<=c_r-1; ++i) sum += b[i]; for (int i=c_r*len; i<=r; ++i) sum += a[i]; } Другие задачиМы рассматривали задачу нахождения суммы элементов массива в каком-то его подотрезке. Эту задачу можно немного расширить: разрешим также меняться отдельным элементам массива С другой стороны, вместо задачи о сумме аналогично можно решать задачи о минимальном, максимальном элементах в отрезке. Если в этих задачах допускать изменения отдельных элементов, то тоже надо будет пересчитывать значение Аналогичным образом sqrt-декомпозицию можно применять и для множества других подобных задач: нахождение количества нулевых элементов, первого ненулевого элемента, подсчёта количества определённых элементов, и т.д. Есть и целый класс задач, когда происходят изменения элементов на целом подотрезке: прибавление или присвоение элементов на каком-то подотрезке массива Например, нужно выполнять следующие два вида запросов: прибавить ко всем элементам некоторого отрезка Наконец, можно комбинировать оба вида задач: изменение элементов на отрезке и ответ на запросы тоже на отрезке. Оба вида операций будут выполняться за Можно привести пример и других задач, к которым можно применить sqrt-декомпозицию. Например, можно решать задачу о поддержании множества чисел с возможностью добавления/удаления чисел, проверки числа на принадлежность множеству, поиск Sqrt-декомпозиция входных запросовРассмотрим теперь совершенно иное применение идеи об sqrt-декомпозиции. Предположим, что у нас есть некоторая задача, в которой нам даются некоторые входные данные, а затем поступают Конкретная задача может быть весьма сложной, и "честное" её решение (которое считывает один запрос, обрабатывает его, изменяя состояние системы, и возвращает ответ) может быть технически сложным или вовсе быть не по силам для решающего. С другой стороны, решение "оффлайнового" варианта задачи, т.е. когда отсутствуют модифицирующие операции, а имеются только лишь запрашивающие запросы — часто оказывается гораздо проще. Предположим, что мы умеем решать "оффлайновый" вариант задачи, т.е. строить за некоторое время Тогда разобьём входные запросы на блоки (какой длины — пока не уточняем; обозначим эту длину через Теперь будем по очереди брать запросы из текущего блока и обрабатывать каждый из них. Если текущий запрос — модифицирующий, то пропустим его. Если же текущий запрос — запрашивающий, то обратимся к структуре данных для оффлайнового варианта задачи, но предварительно учтя все модифицирующие запросы в текущем блоке. Такое учитывание модифицирующих запросов бывает возможным далеко не всегда, и оно должно происходить достаточно быстро — за время Таким образом, если всего у нас Поскольку приведённые выше рассуждения слишком абстрактны, приведём несколько примеров задач, к которым применима такая sqrt-декомпозиция. Пример задачи: прибавление на отрезкеУсловие задачи: дан массив чисел Хотя эту задачу можно решать и без этого приёма с разбиением запросов на блоки, мы приведём её здесь — как простейшее и наглядное применение этого метода. Итак, разобьём входные запросы на блоки по Таким образом, мы научились отвечать на запрашивающие запросы за время Осталось только заметить, что в конце каждого блока запросов мы должны применить все модифицирующие запросы этого блока к массиву Таким образом, итоговая асимптотика решения составит Пример задачи: disjoint-set-union с разделениемЕсть неориентированный граф с Если бы запросы удаления отсутствовали, то решением задачи была бы известная структура данных disjoint-set-union (система непересекающихся множеств). Однако при наличии удалений задача значительно усложняется. Сделаем следующим образом. В начале каждого блока запросов посмотрим, какие рёбра в этом блоке будут удаляться, и сразу удалим их из графа. Теперь построим систему непересекающихся множеств (dsu) на полученном графе. Как мы теперь должны отвечать на очередной запрос из текущего блока? Наша система непересекающихся множеств "знает" обо всех рёбрах, кроме тех, что добавляются/удаляются в текущем блоке. Однако удаления из dsu нам делать уже не надо — мы заранее удалили все такие рёбра из графа. Таким образом, всё, что может быть — это дополнительные, добавляющиеся рёбра, которых может быть максимум Следовательно, при ответе на текущий запрашивающий запрос мы можем просто пустить обход в ширину по компонентам связности dsu, который отработает за Оффлайновые задачи на запросы на подотрезках массива и универсальная sqrt-эвристика для нихРассмотрим ещё одну интересную вариацию идеи sqrt-декомпозиции. Пусть у нас есть некоторая задача, в которой есть массив чисел, и поступают запрашивающие запросы, имеющие вид Наконец, введём последнее ограничение: мы считаем, что умеем быстро пересчитывать ответ на запрос при изменении левой или правой границы на единицу. Т.е. если мы знали ответ на запрос Опишем теперь универсальную эвристику для всех таких задач. Отсортируем запросы по паре: Рассмотрим теперь группу запросов с одинаковым значением Простым примером на данную эвристику является такая задача: узнать количество различных чисел в отрезке массива Чуть более усложнённым вариантом этой задачи является задача с одного из раундов Codeforces.
|