Часто в задачах требуется посчитать что-то по простому модулю (чаще всего
Обычные арифметические операции выполняются не сильно сложнее — просто нужно брать модули и заботиться о переполнении. Например:
c = (a + b) % mod;
c = (mod + a - b) % mod;
c = a * b % mod;
Но вот с делением возникают проблемы — мы не можем просто взять и поделить. Пример:
Нужно найти некоторый элемент, который будет себя вести как
Если модуль
Теорема.
Доказательство. (для понимания несущественно, можно пропустить)
$$
\begin{aligned}
a^p &= (\underbrace{1+1+\ldots+1+1}\text{$a$ раз})^p
\ &= \sum{x_1+x_2+\ldots+x_a = p} P(x_1, x_2, \ldots, x_a) & \text{(раскладываем по определению)}
\ &= \sum_{x_1+x_2+\ldots+x_a = p} \frac{p!}{x_1! x_2! \ldots x_a!} & \text{(какие слагаемые не делятся на
Здесь
Теперь два раза «поделим» наш результат на
Получается, что
Приведем код, который позволяет считает
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;
}
Диофантовыми уравнениями называют такие штуки:
Требуется решить их в целых числах, то есть
Подставим в качестве
Одним из решений уравнения и будет
Преимущества этого метода над возведением в степень:
- Если обратное существует, то оно найдется даже если модуль не простой. Способ с бинарным возведением тоже можно заставить работать с произвольным модулем, но это будет намного труднее.
- Алгоритм проще выполнять руками.
Сам автор почти всегда использует возведение в степень.
- Это выражение довольно легко вбивать (
1e9+7
). - Простое число.
- Достаточно большое.
int
не переполняется при сложении.long long
не переполняется при умножении.
Кстати,
Пусть нам нужно зачем-то посчитать все те же
Если у нас уже написан inv
, то нам не жалко потратить
После этого мы будем считать
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: техника с сайта емакса.