Мо с изменениями
Третья координата — время. Шесть операций вместо четырёх, блок n в степени две трети и оценка n в степени пять третьих.
3 мин
Обычный алгоритм Мо требует, чтобы массив не менялся: состояние окна должно меняться маленькими шагами, а изменение элемента ломает это. Ответы, посчитанные до изменения и после, относятся к разным массивам.
Починка неожиданно простая: добавить к запросу третью координату.
Время как координата
Пронумеруем изменения по порядку. Каждому запросу припишем время - сколько изменений произошло до него. Теперь состояние описывается тройкой , и операций становится шесть: сдвинуть левую границу, сдвинуть правую, сдвинуть время - каждое в обе стороны.
Сдвиг времени - это применение или откат одного изменения:
struct Change { int pos, value; };
auto applyTime = [&](int i) { // и применение, и откат
Change& u = changes[i];
if (curL <= u.pos && u.pos <= curR) { del(a[u.pos]); add(u.value); }
std::swap(a[u.pos], u.value); // обмен делает операцию обратной себе
};
Обмен вместо присваивания - главная строчка. После него та же самая функция откатывает изменение назад: в changes[i].value теперь лежит старое значение. Переход по времени в обе стороны пишется один раз:
while (time < query.t) applyTime(time++);
while (time > query.t) applyTime(--time);
Проверка «попадает ли изменённая позиция в текущее окно» обязательна: изменение вне окна на счётчики не влияет, но массив поменять всё равно надо - иначе последующий сдвиг границы прочитает не то значение.
Порядок обхода и размер блока
Сортируем по тройке: номер блока , номер блока , время.
std::sort(queries.begin(), queries.end(), [B](const Ask& x, const Ask& y) {
int bx = x.l / B, by = y.l / B;
if (bx != by) return bx < by;
int rx = x.r / B, ry = y.r / B;
if (rx != ry) return (bx & 1) ? rx > ry : rx < ry;
return (rx & 1) ? x.t > y.t : x.t < y.t;
});
Оценка складывается из трёх слагаемых:
| указатель | сколько ходит |
|---|---|
| левый | |
| правый | |
| время |
Третья строчка - потому что внутри пары блоков время движется монотонно, а таких пар . Сумма минимальна при
Сколько это на самом деле
Оценка выглядит безобидно, пока не подставишь числа:
На версия с изменениями даже дешевле обычной - но там и без Мо всё проходит. А начиная с разница становится решающей: если в задаче есть изменения и больше сотни тысяч, Мо скорее всего не тот инструмент.
Ограничения в условии - подсказка. Задача на Мо с изменениями почти всегда даёт и до , и это не случайность.
Подробнее про каркас: «Алгоритм Мо».