Алгоритм Мо: различия между версиями
Ctrlalt (обсуждение | вклад) (→Ссылки) |
Ctrlalt (обсуждение | вклад) Нет описания правки |
||
| (не показаны 2 промежуточные версии этого же участника) | |||
| Строка 11: | Строка 11: | ||
1. Мысленно делим массив на sqrt(N) блоков длины sqrt(N); для каждого запроса вычисляем номер блока, в который попадает его левый конец: | 1. Мысленно делим массив на sqrt(N) блоков длины sqrt(N); для каждого запроса вычисляем номер блока, в который попадает его левый конец: | ||
struct Query { | struct Query { | ||
int | int l, r, block, index; | ||
} | }; | ||
int blockSize = sqrt( | int blockSize = sqrt(a.size()); | ||
for (int i = 0; i < | vector<Query> queries(queryCount); | ||
for (int i = 0; i < queryCount; i++) { | |||
int queryL, queryR; | |||
queries[i] | cin >> queryL >> queryR; | ||
queries[i] = { queryL, queryR, queryL / blockSize, i }); | |||
} | } | ||
2. Сортируем запросы сначала по возрастанию блока, затем по возрастанию правого конца: | 2. Сортируем запросы сначала по возрастанию блока, затем по возрастанию правого конца: | ||
bool | bool operator < (const Query &that) const { | ||
if (block != that.block) | |||
return block < that.block; | |||
return r < that.r; | |||
} | } | ||
sort(queries, queries | sort(queries.begin(), queries.end()); | ||
3. Заводим индексы l и r для начала и конца рабочего отрезка, рассчитываем ответ на рабочем отрезке: | 3. Заводим индексы l и r для начала и конца рабочего отрезка, рассчитываем ответ на рабочем отрезке: | ||
int l = 0, r = 0 | Counter counter; | ||
int l = 0, r = 0; | |||
counter.add(a[l], 1); | |||
4. Обрабатываем запросы в порядке сортировки, втупую двигая l и r куда нужно и обновляя ответ для рабочего отрезка: | 4. Обрабатываем запросы в порядке сортировки, втупую двигая l и r куда нужно и обновляя ответ для рабочего отрезка: | ||
for ( | vector<int> res(queries.size()); | ||
while (l | for (auto &[queryL, queryR, block, index] : queries) { | ||
. | while (queryL < l) | ||
counter.add(a[--l], 1); | |||
while (l < queryL) | |||
while (l | counter.add(a[l++], -1); | ||
while (queryR < r) | |||
. | counter.add(a[r--], -1); | ||
while (r < queryR) | |||
while (r | counter.add(a[++r], 1); | ||
res[index] = counter.getRes(); | |||
. | |||
while (r | |||
. | |||
} | } | ||
Обрати внимание: в некоторых задачах важен порядок операций: например, сначала расширяем рабочий отрезок, затем сужаем. | Обрати внимание: в некоторых задачах важен порядок операций: например, сначала расширяем рабочий отрезок, затем сужаем. | ||
== | == Сложность == | ||
Предполагаем, что | Предполагаем, что ответ обновляется за O(1). | ||
Всего обрабатывается sqrt(N) блоков. Внутри каждого блока: | Всего обрабатывается sqrt(N) блоков. Внутри каждого блока: | ||
* l при каждом запросе | * l при каждом запросе может сдвигаться влево или вправо, но каждый раз не более чем на sqrt(N). Всего по всем блокам — не более Qsqrt(N) раз (Q — количество запросов); | ||
* r | * r может сдвигаться только вправо, не более N раз для блока. Всего по всем блокам — не более Nsqrt(N) раз. | ||
Итоговая асимптотика '''(Q + N)sqrt(N)''' (плюс QlogQ на сортировку запросов). | |||
== Ссылки == | == Ссылки == | ||
| Строка 71: | Строка 71: | ||
* [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/blog/entry/72690 Codeforces — Mo's Algorithm (with and without update)] | * [http://codeforces.com/blog/entry/72690 Codeforces — Mo's Algorithm (with and without update)] | ||
* [https://codeforces.com/blog/entry/81716 Codeforces — Everything on Mo's Algorithm] | |||
* [https://codeforces.com/blog/entry/83630 Codeforces — Два варианта написания алгоритма Мо и 3Д Мо] | |||
Задачи: | Задачи: | ||
* [http://codeforces.com/contest/86/problem/D Codeforces 86.D] | * [http://codeforces.com/contest/86/problem/D Codeforces 86.D] | ||
Текущая версия от 10:51, 23 августа 2026
Когда применяется
- Тяжёлые запросы на отрезках:
- Количество различных элементов на отрезке
- Наиболее частый элемент на отрезке
- Количество чисел x, встречающихся x раз на отрезке
- Количество инверсий на отрезке
- Элементы массива не меняются;
- Ответ на запросы в оффлайне (запросы будут сортироваться, отвечать будем не в том порядке, в котором они даны).
Что делаем
1. Мысленно делим массив на sqrt(N) блоков длины sqrt(N); для каждого запроса вычисляем номер блока, в который попадает его левый конец:
struct Query {
int l, r, block, index;
};
int blockSize = sqrt(a.size());
vector<Query> queries(queryCount);
for (int i = 0; i < queryCount; i++) {
int queryL, queryR;
cin >> queryL >> queryR;
queries[i] = { queryL, queryR, queryL / blockSize, i });
}
2. Сортируем запросы сначала по возрастанию блока, затем по возрастанию правого конца:
bool operator < (const Query &that) const {
if (block != that.block)
return block < that.block;
return r < that.r;
}
sort(queries.begin(), queries.end());
3. Заводим индексы l и r для начала и конца рабочего отрезка, рассчитываем ответ на рабочем отрезке:
Counter counter; int l = 0, r = 0; counter.add(a[l], 1);
4. Обрабатываем запросы в порядке сортировки, втупую двигая l и r куда нужно и обновляя ответ для рабочего отрезка:
vector<int> res(queries.size());
for (auto &[queryL, queryR, block, index] : queries) {
while (queryL < l)
counter.add(a[--l], 1);
while (l < queryL)
counter.add(a[l++], -1);
while (queryR < r)
counter.add(a[r--], -1);
while (r < queryR)
counter.add(a[++r], 1);
res[index] = counter.getRes();
}
Обрати внимание: в некоторых задачах важен порядок операций: например, сначала расширяем рабочий отрезок, затем сужаем.
Сложность
Предполагаем, что ответ обновляется за 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 — Алгоритм Мо
- algocode.ru — Алгоритм Мо
- blog.anudeep2011.com — Mo's algorithm
- hackerearth.com — Mo's algorithm
- hackerrank.com — Mo's algorithm
- Codeforces — Mo's Algorithm (with and without update)
- Codeforces — Everything on Mo's Algorithm
- Codeforces — Два варианта написания алгоритма Мо и 3Д Мо
Задачи:
- Codeforces 86.D
- Codeforces 220.B
- SPOJ DQUERY
- CodeChef IITI1 (понадобится дерамида или сжатие координат + дерево Фенвика)