Мо онлайн на примере задачи с недавнего Educational Round

Правка ru26, от Noobish_Monk, 2025-07-17 12:58:41

Всем привет!

На контестах периодически попадаются задачи на запросы на отрезках. Большая часть задач даёт запросы в оффлайне, что даёт большую свободу действий, но всё же часто они сводятся к дереву отрезков, а иногда и алгоритму Мо. Однако иногда в задачах может оказаться так, что оффлайн её решает простой Мо, а в онлайне она становится интересной. В таких ситуациях некоторые люди пытаются поддерживать что-то типа $$$500$$$ разных состояний алгоритма Мо, я вообще не понимал, что имеется в виду.

Сегодня же я сумел придумать версию онлайн Мо, работающую за $$$O(n^{\frac{5}{3}})$$$ времени и памяти в нормальных задачах (в которых движение границ делается за $$$O(1)$$$). Для анализа асимптотики я буду считать, что $$$n = q$$$. Я не первый, кто такую идею придумал. Как оказалось, здесь и здесь было обсуждение, где такая идея была предложена, но я хочу сам о ней рассказать, потому что я о ней раньше не знал.

В этом блоге я хочу разобрать, как с помощью онлайн Мо можно решить задачу с недавнего Educational раунда.

2043G - Задача на запросы

Тут слишком много всего наворочено — запросы изменения, да ещё и закодированы. Попробуем сначала решить версию оффлайн и без запросов обновления. Стоит оговориться, что я буду считать не количество индексов $$$i \lt j$$$ таких, что $$$a_i \neq a_j$$$, а наоборот, что $$$a_i = a_j$$$. Из этой величины легко получить ответ на исходную задачу, однако количество пар индексов с равными значениями поддерживать несколько проще.

Сама задача — несложное упражнение на алгоритм Мо.

Для тех, кому интересна реализация

Я хочу взглянуть на анализ асимптотики с геометрической стороны. Размер блока для алгоритма Мо обозначим за $$$B \approx \sqrt{n}$$$. Вкратце, мы делим плоскость на квадратики со стороной $$$B$$$ и обходим квадратики в правильном порядке.

Анализ асимптотики

Теперь добавим ограничение в виде онлайн запросов. К сожалению в этой версии мы не можем выбирать порядок обхода квадратиков, да и в каждый квадратик нам может понадобиться заходить несколько раз, поэтому нужен другой подход. Понятно, что лишь одной парой указателей нам не обойтись, поскольку в таком случае на каждый запрос мы будем отвечать за $$$O(n)$$$, что долго. Назовём состоянием всю необходимую информацию, которую мы обычно поддерживаем для одного отрезка. В данном случае это массив количеств $$$cnt$$$, количество пар равных индексов $$$sum$$$ и указатели $$$L$$$ и $$$R$$$. Я хочу создать несколько состояний, чтобы от какого-то из них можно было быстро дойти до нашего запроса.

Выберем $$$B \approx n^{\frac{2}{3}}$$$. Теперь у нас мало квадратиков, всего $$$\frac{n^2}{B^2} = n^{\frac{2}{3}}$$$. Я хочу сделать так, чтобы в каждом квадратике было состояние, чтобы от него было недалеко идти до очередного запроса. Действительно, внутри квадратика расстояние между точками не превосходит $$$2B$$$, а это уже приемлемое время работы для запроса. Алгоритм можно описать так:

Определим квадратик, в котором находится запрос.

  • Если в нём ранее не было запросов, то можно за $$$O(n)$$$ инициализировать этот блок. Создадим состояние, например, в точке самого запроса.
  • Иначе у нас есть состояние на расстоянии не более $$$2B$$$, поэтому мы можем за $$$O(B)$$$ ответить на запрос.

Так как блоков мало, суммарно все инициализации отработают за $$$O(n^{\frac{5}{3}})$$$, а суммарное количество движений указателей не превосходит $$$O(qB) = O(nB) = O(n^{\frac{5}{3}})$$$.

Отлично, мы научились делать Мо в онлайне быстрее чем за $$$O(nq)$$$. А можно ли прикрутить запросы изменения? И если да, то надо ли нам чем-либо жертвовать? Оказывается, ответы на эти вопросы — да и нет! Обычно при запросах изменения используется 3D Мо, который вводит время в качестве третьего измерения, и обходится уже не плоскость, а трёхмерное пространство. Тогда чтобы двигать третий параметр $$$T$$$, нужно проверить, лежит ли изменяемая позиция в текущем состоянии, и если да, то сделать del и add. Но нам ведь ничего не мешает сделать то же самое, просто для всех блоков. Поскольку их, опять же, мало, $$$O(n^{\frac{2}{3}})$$$, время работы никак не ухудшается.

С помощью такой идеи понятно, как развалить задачу. Да и пишется это довольно легко.

Моё решение: 329249141

Это просто феноменально, имхо, что настолько простая идея, как алгоритм Мо, может работать в онлайне, да ещё и с изменениями!

P. S.: Правда, как оказалось, это некоторая интерпретация разбора задачи, а не другое решение. Разбиение на квадратики — то же самое, что и разбиение на блоки. Инициализация состояния в каждом квадратике эквивалентна предпосчёту ответа для каждой пары блоков, а движение указателей — добавление элементов с краёв неполных блоков.

История

 
 
 
 
Правки
 
 
  Rev. Язык Кто Когда Δ Комментарий
ru26 Русский Noobish_Monk 2025-07-17 12:58:41 0 (опубликовано)
en15 Английский Noobish_Monk 2025-07-17 12:58:31 0 (published)
en14 Английский Noobish_Monk 2025-07-17 12:58:12 14 Tiny change: 'e remains unaffected. \n\nWit' -> 'e remains the same. \n\nWit'
en13 Английский Noobish_Monk 2025-07-17 12:56:17 55
en12 Английский Noobish_Monk 2025-07-17 12:55:39 17
ru25 Русский Noobish_Monk 2025-07-17 12:55:12 13
ru24 Русский Noobish_Monk 2025-07-17 12:53:22 134
en11 Английский Noobish_Monk 2025-07-17 12:53:18 1016
ru23 Русский Noobish_Monk 2025-07-17 12:26:33 242
en10 Английский Noobish_Monk 2025-07-17 12:00:00 0 (saved to drafts)
ru22 Русский Noobish_Monk 2025-07-17 11:59:41 597 (сохранено в черновиках)
en9 Английский Noobish_Monk 2025-07-17 02:34:42 16
ru21 Русский Noobish_Monk 2025-07-17 02:24:50 0 (опубликовано)
en8 Английский Noobish_Monk 2025-07-17 02:24:43 0 (published)
en7 Английский Noobish_Monk 2025-07-17 02:24:27 2
ru20 Русский Noobish_Monk 2025-07-17 02:24:22 4 Мелкая правка: 'о сделать del и add. Но нам в' -> 'о сделать *del* и *add*. Но нам в'
en6 Английский Noobish_Monk 2025-07-17 02:22:40 57
en5 Английский Noobish_Monk 2025-07-17 02:21:49 5
en4 Английский Noobish_Monk 2025-07-17 02:21:06 40
en3 Английский Noobish_Monk 2025-07-17 02:20:35 10
en2 Английский Noobish_Monk 2025-07-17 02:20:21 4812
en1 Английский Noobish_Monk 2025-07-17 02:18:54 1582 Initial revision for English translation (saved to drafts)
ru19 Русский Noobish_Monk 2025-07-17 02:16:04 125 Мелкая правка: ' Создадим \textit{состояние}, например' -> ' Создадим _состояние_, например'
ru18 Русский Noobish_Monk 2025-07-17 02:13:30 1 Мелкая правка: ' Создадим \textit{состояние}, например' -> ' Создадим _состояние_, например'
ru17 Русский Noobish_Monk 2025-07-17 02:13:02 11 Мелкая правка: ' Создадим \textit{состояние}, например' -> ' Создадим _состояние_, например'
ru16 Русский Noobish_Monk 2025-07-17 02:12:46 71
ru15 Русский Noobish_Monk 2025-07-17 02:11:35 35
ru14 Русский Noobish_Monk 2025-07-17 02:08:01 9 Мелкая правка: 'n[cut]\n\n\nPrerequisites: Мо и желательно 3D Мо\n\nВ этом' -> 'n[cut]\n\nВ этом'
ru13 Русский Noobish_Monk 2025-07-17 02:07:23 18 Мелкая правка: 'n[cut]\n\n\nPrerequisites: Мо и желательно 3D Мо\n\nВ этом' -> 'n[cut]\n\nВ этом'
ru12 Русский Noobish_Monk 2025-07-17 02:06:29 42 Мелкая правка: 'n[cut]\n\n\nPrerequisites: Мо и желательно 3D Мо\n\nВ этом' -> 'n[cut]\n\nВ этом'
ru11 Русский Noobish_Monk 2025-07-17 02:05:26 50
ru10 Русский Noobish_Monk 2025-07-17 02:05:01 2
ru9 Русский Noobish_Monk 2025-07-17 02:04:43 64
ru8 Русский Noobish_Monk 2025-07-17 02:03:25 23
ru7 Русский Noobish_Monk 2025-07-17 02:02:38 2500
ru6 Русский Noobish_Monk 2025-07-17 01:48:38 2209
ru5 Русский Noobish_Monk 2025-07-17 01:21:15 84
ru4 Русский Noobish_Monk 2025-07-16 22:48:03 93
ru3 Русский Noobish_Monk 2025-07-16 22:46:01 1061
ru2 Русский Noobish_Monk 2025-07-16 22:34:01 1016
ru1 Русский Noobish_Monk 2025-07-16 22:08:23 1025 Первая редакция (сохранено в черновиках)