Всем привет!
Сегодняшний пост посвящён задаче F с недавнего Div 1 + Div 2 раунда. Она стала локальной знаменитостью, потому что является более решаемой задачей, чем задача Е того же раунда. А сегодня мы разберёмся, почему же эта задача такая простая
Итак, переходим к задаче. В ней для каждого отсортированного массива $$$a$$$ с элементами от $$$1$$$ до $$$m$$$ нужно посчитать суммарное количество итераций, за которое стандартный бинпоиск находит в массиве каждый элемент от $$$1$$$ до $$$m$$$. В задаче эта сумма описана как $$$f(a,1,1,n)+f(a,2,1,n)+\ldots+f(a,m,1,n)$$$. Ограничение: $$$n, m \leq 10^6$$$
Давайте попробуем что-то понять про количество итераций бинпоиска в зависимости от искомого числа $$$k$$$. Интуиция подсказывает, что на всех массивах бинпоиск будет вести себя регулярным образом, нужно это лишь формализовать. Для того, чтобы быстрее нащупать идею, давайте немного упростим задачу и будем считать, что все числа в массиве $$$a$$$ различны. Это действительно удобно, так как в таком случае у каждого элемента $$$k$$$ в массиве есть единственное вхождение, на котором остановится бинпоиск, если будет его искать.
Итак, мы сделали удобное предположение того, что все числа различные и мы знаем, что у каждого числа $$$k$$$ в массиве есть своя позиция, обозначим её за $$$pos[k]$$$. Если число $$$k$$$ не встречается в массиве $$$a$$$, то оно не влияет на сумму, так что такие случаи можно не рассматривать.
Вопрос 1: Как же посчитать $$$f(a,k,1, n)$$$ в таком случае?
Подсказка 1.1Поймите, что ответ зависит только от $$$pos[k]$$$, а не от самого $$$k$$$
Ответ 1.1Действительно, бинарный поиск будет искать элемент, находящийся на позиции $$$pos[k]$$$ и сделает точно такие же шаги вне зависимости от самого значения $$$k$$$, если массив $$$a$$$ отсортирован. Осталось только посчитать для каждой позиции за сколько шагов бинпоиск до неё дойдёт
Подсказка 1.2Это совсем уж не сложно :)
Ответ 1.2Давайте просто промоделируем бинпоиск как написано в задаче, только мы будем заходить и в левый и в правый отрезок и запоминать для каждого числа на каком по очереди шаге оно стало медианой рассматриваемого отрезка. Такой алгоритм только подтверждает, что количество итераций зависит от позиции, а не от значения $$$k$$$
Итак, мы научились для одного массива считать значение $$$f(a,k,1, n)$$$ для любого $$$k$$$ и даже поняли некоторые особенности, которые позволяют свернуть сумму и посчитать её "на листочке"
Вопрос 2: Как же посчитать сумму $$$f(a,k,1, n)$$$ по всем $$$k$$$ в отсортированном массиве из различных элементов?
Подсказка 2.1Попробуйте посмотреть на эту сумму не через значения $$$k$$$, а через их позиции в массиве
Ответ 2.1Так как $$$f(a,k,1, n)$$$ зависит только от позиции числа $$$k$$$ в массиве и мы считаем сумму по всем $$$k$$$, это аналогично сумме по всем позициям для массива из различных чисел, так что
Подсказка 2.2Так как мы совсем не опирались на значения массива, и использовали лишь строгое возрастание, то мы можем быстро просуммировать полученные суммы для всех массивов $$$a$$$
Ответ 2.2Действительно, для каждого строго возрастающего массива сумма значений $$$f$$$ одинакова, поэтому нужно будет лишь умножить сумму для одного массива на количество таких массивов. Здесь становится понятно, что нужно возвращаться к массивам с повторяющимися числами
Теперь наших идей достаточно, чтобы вернуться к общему случаю
Вопрос 3: Как теперь посчитать сумму для всех массивов $$$a$$$, а не только для строго возрастающих?
Подсказка 3.1В более простом случае нам помогал взгляд на сумму через позицию в массиве, на которой бинпоиск останавливается. Давайте попробуем здесь посчитать, на скольких массивах бинпоиск остановится ровно на позиции $$$i$$$
Ответ 3.1В отличие от строго возрастающих массивов, где для каждой позиции есть бинпоиск, который в ней останавливается, в общем случае это неверно. Например, если массив состоит только из единиц, бинпоиск остановится только на индексе $$$\frac{n - 1}{2}$$$ (в 0-нумерации). На самом деле, бинпоиск, который ищет число $$$k$$$, остановится в позиции $$$i$$$, если $$$a_i = k$$$ и он не остановился ранее. К счастью, до того, как прийти в позицию $$$i$$$, бинпоиск проверял конкретный набор позиций, зависящий только от $$$i$$$. Обозначим его $$$Poses(i)$$$
Итого, нам нужно научиться считать количество массивов, в которых элемент с индексом $$$i$$$ не совпадает ни с одним из элементов с индексом из $$$Poses(i)$$$. Как бы это посчитать?
Подсказка 3.2Давайте сначала попробуем упростить условие. Несмотря на то, что $$$|Poses(i)|$$$ порядка $$$log(n)$$$, это всё равно много и неудобно
Ответ 3.2Заметим, что массив $$$a$$$ монотонный, а значит, что если для некоторого $$$j \in Poses(i)$$$ и $$$j \lt i$$$ выполнено $$$a_j = a_i$$$, то и для самого правого $$$j \lt i$$$ тоже, а значит можно оставить только его. Аналогично выкинем все $$$j \gt i, j \in Poses(i)$$$, кроме самого левого из них. У нас останется лишь два индекса, обозначим их за $$$l_i$$$ и $$$r_i$$$ соответственно. Замечу также, что они могут и не быть определены. В конце концов, индексов $$$j \lt i$$$ может не существовать
Подсказка 3.3Кажется, что посчитать количество массивов с условием, что значения на каких-то двух индексах не равны значению на третьем индексе — сложновато. А если условие заменить на равенство, то, наоборот, мы уже будем знать, что все промежуточные числа тоже совпадают с $$$a_i$$$. Давайте попробуем посчитать количество всех массивов и вычесть из них неподходящие
Ответ 3.3Количество всех массивов считается довольно стандартным образом. Давайте сделаем разумую вещь для монотонным массивов — перейдём к массиву разностей $$$d$$$, где $$$d_0$$$ = $$$a_0 - 1$$$, $$$d_i = a_i - a_{i-1}$$$, $$$d_n = m - a_{n-1}$$$. На него есть два условия: $$$d_i \geq 0$$$ и $$$\sum\limits_{i=0}^n d_i = m - 1$$$. А уже количество таких массивов можно вычислить через задачу о шарах и перегородках, по сути это она и есть — если не слышали ранее — обязательно ознакомьтесь, это очень стандартная комбинаторная задача. При помощи предподсчёта факториалов мы сможем считать число сочетаний $$$C(n, k)$$$ за $$$O(1)$$$ и без труда найдём общее число массивов.
А что насчёт количества неподходящих массивов? Если все элементы с $$$a_{l_i}$$$ по $$$a_i$$$ равны, это означает, что в массиве разностей $$$d_{l_i+1} = d_{l_i + 2} = \ldots = d_i$$$, а значит это количество равно количеству массивов с той же суммой $$$m-1$$$, но уже не из $$$n$$$, а из $$$n - (i - l_i)$$$ элементов, так как все оставшиеся равны нулю. И, чтобы формула стала полностью верной, нужно вычесть такое же количество для $$$r_i$$$. Но при этом мы два раза вычтем количество массивов, для которых все элементы с $$$l_i$$$ по $$$r_i$$$ совпадают. Давайте добавим их обратно, посчитав по уже знакомой формуле
Итак, мы научились считать сумму для всех массивов, давайте оценим время работы:
Так как нам понадобятся C-шки с индексами порядка $$$n + m$$$, мы за $$$O(n + m)$$$ посчитаем все факториалы и обратные факториалы для быстрых вычислений. Я в своём решении предподсчитал факториалы до $$$2 \cdot 10^6$$$ перед всеми мультитестами. И само решение для каждой позиции считает сколько шагов нужно, чтобы бинпоиск до неё дошёл, а также количество массивов, для которых бинпоиск остановится на ней. Всё это суммарно за $$$O(n)$$$. Значит итоговое время работы $$$O(n + m)$$$
Как видите, в здааче не было большого числа идей и она не зря была так популярна на раунде. Надеюсь, вам понравился этот разбор и принёс пользу
Замечание: Если Вам понравился это пост, то, возможно, Вам понравится контент из телеграм канала, в который я выкладываю свою подготовку к финалу ICPC 2026. Переходите по ссылке