Сайт переезжает. Большинство статей уже перенесено на новую версию.
Скоро добавим автоматические переходы, но пока обновленную версию этой статьи можно найти там.

Обратный элемент по модулю

Часто в задачах требуется посчитать что-то по простому модулю (чаще всего 109+7). Это делают для того, чтобы участникам не приходилось использовать длинную арифметику, и они могли сосредоточиться на самой задаче.

Обычные арифметические операции выполняются не сильно сложнее — просто нужно брать модули и заботиться о переполнении. Например:

c = (a + b) % mod;
c = (mod + a - b) % mod;
c = a * b % mod;

Но вот с делением возникают проблемы — мы не можем просто взять и поделить. Пример: 82=4, но 8%5=32%5=24.

Нужно найти некоторый элемент, который будет себя вести как 1a=a1, и вместо «деления» домножать на него. Назовем такой элемент обратным.

Способ 1: бинарное возведение в степень

Если модуль p простой, то решением будет a1ap2. Это следует из малой теоремы Ферма:

Теорема. apa(modp) для всех a, не делящихся на p.

Доказательство. (для понимания несущественно, можно пропустить)

ap=(1+1++1+1a раз)p=x1+x2++xa=pP(x1,x2,,xa)(раскладываем по определению)=x1+x2++xa=pp!x1!x2!xa!(какие слагаемые не делятся на p?)P(p,0,,0)++P(0,0,,p)(все остальные не убьют p в знаменателе)=a

Здесь P(x1,x2,,xn)=k(xi!) это мультиномиальный коеффициент — количество раз, которое элемент a1x1a2x2anxn появится при раскрытии скобки (a1+a2++an)k.

Теперь два раза «поделим» наш результат на a.

apaap11ap2a1

Получается, что ap2 ведет себя как a1, что нам по сути и нужно. Посчитать ap2 можно за O(logp) бинарным возведением в степень.

Приведем код, который позволяет считает Cnk.

int t[maxn]; // факториалы, можно предподситать простым циклом

// бинарное возведение в степень
int bp (int a, int n) {
    int res = 1;
    while (n) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

// находит обратный элемент как a^(p-2)
int inv (int x) {
    return bp(x, mod-2);
}

int c (int n, int k) {
    return t[n] * inv(t[k]) % mod * inv(t[n-k]) % mod;
}

Способ 2: диофантово уравнение

Диофантовыми уравнениями называют такие штуки:

ax+by=1

Требуется решить их в целых числах, то есть a и b известны, и нужно найти такие целые (возможно, отрицательные) x и y, чтобы равенство выполнялось. Решают такие вещи расширенным алгоритмом Евклида. TODO: описать, как он работает.

Подставим в качестве a и b соответственно a и m

ax+my=1

Одним из решений уравнения и будет a1, потому что если взять уравнение по модулю m, то получим

ax+by=1ax1xa1(modm)

Преимущества этого метода над возведением в степень:

Сам автор почти всегда использует возведение в степень.

Почему 109+7?

  1. Это выражение довольно легко вбивать (1e9+7).
  2. Простое число.
  3. Достаточно большое.
  4. int не переполняется при сложении.
  5. long long не переполняется при умножении.

Кстати, 109+9 обладает теми же свойствами. Иногда используют и его.

Предподсчёт обратных факториалов за линейное время

Пусть нам нужно зачем-то посчитать все те же Cnk, но для больших n и k, поэтому асимптотика O(nlogm) нас не устроит. Оказывается, мы можем сразу предподсчитать все обратные ко всем факториалам.

Если у нас уже написан inv, то нам не жалко потратить O(logm) операций, посчитав m!1.

После этого мы будем считать (m1)!1 как m!1m=112(m1).

int f[maxn];
f[0] = 1;
for (int i = 1; i < maxn; i++)
    f[i] = i*f[i-1] % mod;

int r[maxn];
r[maxn-1] = inv(f[maxn-1])
for (int i = maxn-1; i >= 1; i--)
    r[i-1] = r[i]*i % mod;

TODO: техника с сайта емакса.