L. Различные на отрезке
Дан массив. На каждый запрос нужно сказать, сколько различных значений встречается среди .
Обновлений нет, все запросы известны заранее - значит, можно отвечать на них не в том порядке, в котором они заданы. Это и есть алгоритм Мо.
Два указателя, которые не откатываются
Держим текущий отрезок и счётчики cnt[v] - сколько раз значение встречается внутри. Отдельно храним число различных: при cnt[v]++ с нуля до единицы оно растёт, при cnt[v]-- до нуля - падает.
auto add = [&](int i) { if (cnt[a[i]]++ == 0) distinct++; };
auto del = [&](int i) { if (--cnt[a[i]] == 0) distinct--; };
Переход от одного запроса к другому стоит столько, на сколько сдвинулись указатели. Если запросы идут в произвольном порядке, суммарный сдвиг может быть квадратичным.
Порядок, в котором сдвиги малы
Разобьём индексы на блоки длины и отсортируем запросы по паре (блок левого конца, правый конец).
- внутри блока левый конец гуляет по блоку: на запрос, всего ;
- внутри блока правый конец только растёт: на блок, всего .
При обе суммы равны . Обычная корневая тоже проходит.
Змейка
В чётных блоках сортируем по правому концу по возрастанию, в нечётных - по убыванию. Тогда при переходе к следующему блоку правый указатель не откатывается в начало. Это не меняет асимптотику, но на практике ускоряет примерно в полтора раза.
Подробнее: «Алгоритм Мо».
Формат ввода
В первой строке - число ().
Во второй строке - чисел ().
В третьей строке - число запросов ().
В следующих строках - пары и ().
Формат вывода
Для каждого запроса выведите количество различных значений на отрезке.
Примеры
5 1 2 1 3 2 4 1 5 1 3 2 4 3 3
3 2 3 1
6 4 4 4 4 4 4 3 1 6 2 3 5 5
1 1 1