Решаю олимпиадную задачку по программированию, для её решения нужно быстро считать модули биномиальных коэффициентов.
Вот исходные данные:
* Нужно подсчитать
, где
*
*
* Нужно подсчитать 500 коэффициентов за 3 секунды (500 - это количество входных данных), а лучше -- быстрее, т.к. выполняются другие операции
* Для справки: за секунду выполняется
сложений,
умножений и
делений.
Обращаю ваше внимание, что
не нужно считать весь коэффициент, нужно посчитать
только остаток от деления. Это как-то должно ускорить вычисления, но как??
Пока придумал такой алгоритм: рассмотрим способ вычисления
. Ясно, что в числителе и знаменателе одинаковое количество чисел; этим и воспользуемся для того, чтобы избавиться от знаменателя. Будем называть числа в числителе "верхними числами", а в знаменателе -- "нижними". Так вот, ясно, что для каждого нижнего числа есть одно или несколько кратных ему верхних.
Если совпадение однозначно, то удается очень быстро избавиться от знаменателя; останется только числитель. Затем нужно быстро пробежаться по нему, чтобы найти модуль этого произведения. Тогда алгоритму требуется два прохода по массиву верхних чисел и
делений, и
умножений, и он укладывается в те самые 3 секунды.
Однако проблема в том, что для некоторых коэффициентов не получается построить однозначное соответствие: во-первых, среди верхних чисел могут быть простые и им не соответствуют никакие нижние; во-вторых, нижние числа могут либо соответствовать нескольким верхним. Поэтому возникает задача перебора (для подбора верхних чисел, кратных нижним), которая решается достаточно долго.
Вот и получается, что по такому алгоритму я могу считать биномиальные коэффициенты в общем случае за 1 секунду, что в 166 раз хуже, чем надо.
Похоже, нужно как-то опираться на тот факт, что вычисления идут по модулю, но как -- не знаю.
Подскажите, пожалуйста!
На всякий случай, исходная задача -- подсчитать количество перестановок
с неподвижными точками по модулю
, где
, она сводится к вычислению биномиального коэффициента и субфакториала.