← Все вопросы

Почему в задачах берут ответ по модулю 10^9+7 и какие грабли с переполнением long long при умножении?

Задан 24 месяца назад1.6к просмотров2 ответа
8

Постоянно вижу «выведите ответ по модулю 10^9+7». Зачем вообще этот модуль и почему именно простое число? И главная боль: где меня поджидает переполнение long long при работе по модулю и как его системно избегать?

2 ответа

13
✓ Принятый ответ — помог автору

Модуль берут потому, что ответы в комбинаторных задачах растут экспоненциально (число путей, перестановок и т.п.) и не влезают ни в какой целый тип. Брать остаток позволяет работать с фиксированной разрядностью. Модуль 10^9+7 (и его брат 998244353) выбирают потому, что он простой — это критично: при простом m для любого a, не кратного m, существует обратный элемент, а значит можно «делить» по модулю (через малую теорему Ферма). 998244353 дополнительно удобен для NTT.

Грабли с переполнением. Базовое правило: остаток всегда < m ≈ 10^9 < 2^30. Тогда:

  • Сложение (a + b) % m — сумма < 2·10^9, влезает в long long (и даже в unsigned int), но если складываешь много слагаемых без % — переполнишь. Бери % регулярно.
  • Умножение a * b % m — произведение < 10^18 < 2^63, в long long влезает, но только если оба операнда — long long! Если написать int a, b; ... a * b % m — умножение посчитается в int и переполнится. Объявляй переменные long long или приводи: (long long)a * b % m.
  • Вычитание (a - b) % m может дать отрицательное → пиши ((a - b) % m + m) % m.

Каноничный приём — обернуть всё в функции и держать тип long long везде.

5

Удобно завести аккуратную обёртку, чтобы не думать про знаки и переполнение в каждой строке:

const long long MOD = 1e9 + 7;
long long add(long long a, long long b){ return (a + b) % MOD; }
long long sub(long long a, long long b){ return ((a - b) % MOD + MOD) % MOD; }
long long mul(long long a, long long b){ return a % MOD * (b % MOD) % MOD; }

Если модуль большой (например, до ~10^18), даже long long * long long переполнится — тогда умножение делай через __int128: (long long)((__int128)a * b % MOD). На олимпиадах под GCC __int128 доступен почти всегда.

Ваш ответ

Войдите, чтобы ответить на вопрос.