Алгоритм Мо: различия между версиями
Перейти к навигации
Перейти к поиску
Ctrlalt (обсуждение | вклад) |
Ctrlalt (обсуждение | вклад) Нет описания правки |
||
Строка 4: | Строка 4: | ||
:* Наиболее частый элемент на отрезке | :* Наиболее частый элемент на отрезке | ||
:* Количество инверсий на отрезке | :* Количество инверсий на отрезке | ||
:* [http://codeforces.com/ | :* [http://codeforces.com/contest/220/problem/B Количество чисел x, встречающихся x раз на отрезке] | ||
* Элементы массива не меняются; | * Элементы массива не меняются; | ||
* Ответ на запросы в оффлайне (запросы будут сортироваться, отвечать будем не в том порядке, в котором они даны). | * Ответ на запросы в оффлайне (запросы будут сортироваться, отвечать будем не в том порядке, в котором они даны). | ||
Строка 68: | Строка 68: | ||
* [http://www.hackerearth.com/ru/practice/notes/mos-algorithm/ hackerearth.com — Mo's algorithm] | * [http://www.hackerearth.com/ru/practice/notes/mos-algorithm/ hackerearth.com — Mo's algorithm] | ||
* [http://www.hackerrank.com/topics/mos-algorithm hackerrank.com — Mo's algorithm] | * [http://www.hackerrank.com/topics/mos-algorithm hackerrank.com — Mo's algorithm] | ||
Задачи: | |||
* [http://codeforces.com/contest/86/problem/D Codeforces 86.D] | |||
* [http://codeforces.com/contest/220/problem/B Codeforces 220.B] | |||
* [http://www.spoj.com/problems/DQUERY SPOJ DQUERY] |
Версия от 22:30, 2 августа 2019
Когда применяется
- Тяжёлые запросы на отрезках:
- Количество различных элементов на отрезке
- Наиболее частый элемент на отрезке
- Количество инверсий на отрезке
- Количество чисел 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
Задачи: