Блог пользователя Dword

Автор Dword, история, 6 лет назад, По-русски

Здравствуйте. Хотел бы узнать решение следующей задачи. Дан массив a[0..N-1]. Нужно отвечать на запросы двух типов, желательно за O(log^2(N)) на запрос: 1) L R x — найти кол-во различных элементов отрезка массива a[L..R], меньше либо равных x. 2) i x — изменить элемент a[i] на x. Знаю, как находить кол-во различных элементов на отрезке за O(log^2(N)) на запрос. Думал над применением похожей идеи в данной задаче, но ничего нормального так и не придумал. Какие будут идеи?

  • Проголосовать: нравится
  • +3
  • Проголосовать: не нравится

»
6 лет назад, # |
  Проголосовать: нравится +9 Проголосовать: не нравится

http://acm.math.spbu.ru/~sk1/mm/lections/mipt2014-sqrt/mipt-2014-burunduk1.pdf

Такое точно решается корневой декомпозицией, правда временная сложность хуже получается.

  • »
    »
    6 лет назад, # ^ |
      Проголосовать: нравится +5 Проголосовать: не нравится

    Ну с помощью корневой очевидное решение. А вот можно ли быстрее?

»
6 лет назад, # |
Rev. 6   Проголосовать: нравится -11 Проголосовать: не нравится

не так понял условие, ниже нормальное решение.

  • »
    »
    6 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится +19 Проголосовать: не нравится

    И как ты собираешься объединять ответ на разных отрезках?

    Если на 1-4, у тебя числа 1-2-2-3 — три разных числа

    а на 5 — 8, у тебя тоже три разных числа 3-4-4-5

    То на их объединении, у тебя количество различных чисел — не их сумма. ( не 6, а 5)

»
6 лет назад, # |
Rev. 5   Проголосовать: нравится +7 Проголосовать: не нравится

Решение за O(log^3(N)) на запрос.

Сожмем все входные данные. Храним дерево фенвика для каждого X. В каждой вершине дерева фенвика храним неявное дерево отрезков. В каждой вершине неявного дерева отрезков храним двоичное дерево поиска (например декартово дерево). Для каждого числа поддерживаем next[i] — позиция следующего такого же числа как и a[i] (или n+1 если такого нет) (как и в случае количество различных элементов на отрезке).

Запрос изменения (i, x): в дереве фенвика на суфиксе сделаем del(i,next[i]) — удалим из всех деревьев отрезков next[i], затем a[i]=x и add(i,next[i]) — добавим во все деревья отрезков наше число. Можно сказать что бы удаляем\добавляем (i, next[i]) в каждом дереве отрезков на суффиксе(но посещая при этом логарифм позиций так как это фенвик).

Запрос количества (l, r, x): присвоим x позицию в сжатом массиве, теперь ответом будет get(x, l, r) на префиксе в дереве фенвика в каждом дереве отрезков находим на отрезке с l по r количество чисел больших x (с помощью двоичного дерева поиска в каждой вершине).

  • »
    »
    6 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится +11 Проголосовать: не нравится

    уж лучше писать за sqrt(n) или даже n/64 битмасками, чем за O(log^3)

    Удалено

  • »
    »
    6 лет назад, # ^ |
      Проголосовать: нравится +12 Проголосовать: не нравится

    Стоит отметить, что сейчас алгоритмы с оценкой O(N·log3(N)) относятся больше к теоретическим алгоритмам. Без учета константы N·log23(N) начинает обгонять только на значениях N от 6·108. Поэтому для решения практических задач на всяких OJ такие алгоритмы не будут оптимальным решением.

»
6 лет назад, # |
  Проголосовать: нравится +11 Проголосовать: не нравится

Если решать задачу как "давайте поддерживать массив next и отвечать на 3D-запрос L <= i <= R, a[i] <= x, next[i] > R", то, видимо, для меняющегося массива a ничего сильно лучше O(log3n) не получится.

Вот, что гуглится:

  1. Для не меняющегося массива 3D-запрос умеют за O(logn). Это вроде классика.
    Можно почитать, например, в лекциях MIT

  2. Для меняющегося массива уже с 2D-запросом есть проблема преодолеть барьер O(log2n) => если сверху навесить дополнительное дерево отрезков, для 3D-запроса будет как раз O(log3n).

Подробно про 2D-запрос для меняющегося массива.

Гуглить нужно по словам "dynamic orthogonal range", а ещё точнее "dynamic orthogonal range counting". Есть много статей на тему "reporting" -- вывести все точки, но нас интересует только "counting" -- посчитать количество.

На выходе получаются две статьи.

a) The Cell Probe Complexity of Dynamic Range Counting (2012)
Даёт нижнюю оценку O((logn / loglogw)2) на weighted orthogonal range counting (т.е. найти не количество, а сумму)

b) Space Efficient Data Structures for Dynamic Orthogonal Range Counting [pdf] [html] (2013)
Даёт структуру данных с O(n) памяти и O((logn / loglogn)2) времени на оба запроса -- подсчёт и изменение.

P.S. Ну а за log3n с одной стороны очень медленно, с другой вроде не сложно -- дерево деревьев деревьев.

»
6 лет назад, # |
  Проголосовать: нравится +17 Проголосовать: не нравится

Если нужно, чтобы и код короткий, и работало быстро, есть решение за O(N0.5) без лишних логов.
Это должно быть даже чуть быстрее log2N при N ≤ 300 000.

Короткое описание решения: дерево отрезков по i, внутри корневая по запросам, внутри корневая по a[i] и next[i].

Подробно:

  1. Когда мы научимся отвечать за n0.5 на запрос посчитать i: a[i] <= x, next[i] > R, то, поместив такую структуру в каждую вершину дерева отрезков по i, сможем считать i: L <= i <= R, a[i] <= x, next[i] > R за N0.5 + (N / 2)0.5 + (N / 4)0.5 + ... = O(N0.5). Теперь научимся отвечать на 2D запрос, n  –  размер конкретной вершины ДО, N  –  размер исходного массива.

  2. Если бы массивы a и next не менялись, можно было бы решать так. Отсортируем пары <a[i], next[i]> сперва по ключу a[i] (массив X), затем по ключу next[i] (массив Y). Теперь каждая пара однозначно задаётся позициями в X, Y  –  xi, yi. Важно, что это числа из [0, n), все xi различны, все yi различны. Нарисуем точки xi, yi на плоскости, разобьём квадрат n × n на клетки k × k, где k = n0.5. Клетки образуют двухмерный массив размера (n / k) × (n / k), на котором можно предподсчитать двухмерные частичные суммы F. Когда приходит запрос на подсчёт i: a[i] <= A, next[i] > R, за O(logn) бинпоиском находим позиции A в X и R в Y, обозначим их x, y. За O(n0.5) округляем x и y к ближайшим числам кратным k. За O(1) обращаемся к F.

  3. Операция a[i] = x меняет две пары <a[i], next[i]>. Перестраивать структуру не будем, вместо этого добавим в стек отложенных операций две старые точки со знаком -1 и две новые со знаком +1.

  4. Когда длина стека станет больше 2n0.5, перестроим структуру за O(n)  –  у нас есть отсортированный старый массив, нужно n0.5 элементов удалить, n0.5 элементов добавить.

  5. Построение структуры в одной вершине ДО занимает O(sort + n), где sort = n, так как можно померджить детей. Итого ДО корневых строится за O(NlogN) и весит O(NlogN).

P.S. Или без лишних логов можно как-то проще?

»
6 лет назад, # |
Rev. 3   Проголосовать: нравится -8 Проголосовать: не нравится

deleted