Алгоритм Мо
Перейти к навигации
Перейти к поиску
Когда применяется
- Тяжёлые запросы на отрезках:
- Количество различных элементов на отрезке
- Наиболее частый элемент на отрезке
- Количество чисел x, встречающихся x раз на отрезке
- Количество инверсий на отрезке
- Элементы массива не меняются;
- Ответ на запросы в оффлайне (запросы будут сортироваться, отвечать будем не в том порядке, в котором они даны).
Что делаем
1. Мысленно делим массив на sqrt(N) блоков длины sqrt(N); для каждого запроса вычисляем номер блока, в который попадает его левый конец:
struct Query { int left, right, block, index; } queries[100010]; int blockSize = sqrt(arraySize); for (int i = 0; i < queriesCount; i++) { scanf("%d%d", &queries[i].left, &queries[i].right); queries[i].block = queries[i].left / blockSize; queries[i].index = i; }
2. Сортируем запросы сначала по возрастанию блока, затем по возрастанию правого конца:
bool compare(Query &a, Query &b) { return tie(a.block, a.right) < tie(b.block, b.right); } sort(queries, queries + queriesCount, compare);
3. Заводим индексы l и r для начала и конца рабочего отрезка, рассчитываем ответ на рабочем отрезке:
int l = 0, r = 0, curAns = ... /* ответ для [0..0] */;
4. Обрабатываем запросы в порядке сортировки, втупую двигая l и r куда нужно и обновляя ответ для рабочего отрезка:
for (int i = 0; i < queriesCount; i++) { while (l < queries[i].left) { ... /* исключаем a[l], обновляем curAns */ l++; } while (l > queries[i].left) { l--; ... /* добавляем a[l], обновляем curAns */ } while (r < queries[i].right) { r++; ... /* добавляем a[r], обновляем curAns */ } while (r > queries[i].right) { ... /* исключаем a[r], обновляем curAns */ r--; } ans[queries[i].index] = curAns; }
Обрати внимание: в некоторых задачах важен порядок операций: например, сначала расширяем рабочий отрезок, затем сужаем.
За сколько это работает
Предполагаем, что curAns обновляется за O(1).
Всего обрабатывается sqrt(N) блоков. Внутри каждого блока:
- l при каждом запросе движется влево/вправо не более чем на sqrt(N). Всего по всем блокам — не более Qsqrt(N) раз (Q — количество запросов);
- r движется только вправо, не более N раз для блока. Всего по всем блокам — не более Nsqrt(N) раз.
Итого асимптотика (Q + N)sqrt(N) (плюс QlogQ на сортировку).
Ссылки
Теория:
- neerc.ifmo.ru/wiki — Алгоритм Мо
- ipc.susu.ru — Алгоритм Мо
- blog.anudeep2011.com — Mo's algorithm
- hackerearth.com — Mo's algorithm
- hackerrank.com — Mo's algorithm
- Codeforces — Mo's Algorithm
Задачи:
- Codeforces 86.D
- Codeforces 220.B
- SPOJ DQUERY
- CodeChef IITI1 (понадобится дерамида или сжатие координат + дерево Фенвика)