Алгоритм Мо: различия между версиями
Перейти к навигации
Перейти к поиску
Ctrlalt (обсуждение | вклад) (Новая страница: «== Когда применяется == * Тяжёлые запросы на отрезках: :* Количество инверсий на отрезке; :* Н…») |
Ctrlalt (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
== Когда применяется == | == Когда применяется == | ||
* Тяжёлые запросы на отрезках: | * Тяжёлые запросы на отрезках: | ||
:* Количество | :* [http://www.spoj.com/problems/DQUERY/ Количество различных элементов на отрезке] | ||
:* Наиболее частый элемент на отрезке | :* Наиболее частый элемент на отрезке | ||
:* [http://codeforces.com/problemset/problem/220/B Количество чисел x, встречающихся x раз на отрезке | :* Количество инверсий на отрезке | ||
:* [http://codeforces.com/problemset/problem/220/B Количество чисел x, встречающихся x раз на отрезке] | |||
* Элементы массива не меняются; | * Элементы массива не меняются; | ||
* Ответ на запросы в оффлайне (запросы будут сортироваться, отвечать будем не в том порядке, в котором они даны). | * Ответ на запросы в оффлайне (запросы будут сортироваться, отвечать будем не в том порядке, в котором они даны). |
Версия от 22:10, 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 на сортировку).
Ссылки
Теория: