Разложение на множители и делители
Как разложить число до корня, почему делителей мало и как посчитать их число, не перебирая.
3 мин
Разложение до корня
vector<pair<long long, int>> factorize(long long n) {
vector<pair<long long, int>> result;
for (long long p = 2; p * p <= n; p++) {
if (n % p) continue;
int power = 0;
while (n % p == 0) { n /= p; power++; }
result.push_back({p, power});
}
if (n > 1) result.push_back({n, 1}); // остаток тоже простой
return result;
}
Две вещи, на которых спотыкаются.
Условие цикла проверяет текущий остаток, а не исходное число. По мере деления n уменьшается, и корень уменьшается вместе с ним — цикл заканчивается раньше.
Остаток в конце. Если после цикла n > 1, это простое число, большее корня от исходного. Забыть про него — значит потерять самый крупный множитель. У числа все делители до корня маленькие, а весь ответ сидит именно в остатке.
Проверять делимость на составные числа не вредно: к моменту, когда цикл дойдёт до четвёрки, все двойки уже поделены, и остаток на четыре не разделится.
Делители
Все делители числа перебираются до корня парами: если делит , то и делит.
vector<long long> divisors(long long n) {
vector<long long> result;
for (long long d = 1; d * d <= n; d++) {
if (n % d) continue;
result.push_back(d);
if (d != n / d) result.push_back(n / d); // точный квадрат — не дублируем
}
return result;
}
Проверка d != n / d нужна ровно для точных квадратов: у 36 делитель 6 нашёлся бы дважды.
Сколько делителей бывает
Из разложения число делителей считается формулой:
Каждый делитель — это выбор степени для каждого простого, независимо от остальных.
Величина эта растёт очень медленно. У чисел до максимум — 1344 делителя, до — 103 680. То есть делителей всегда мало, и хранить их списком не страшно.
Практический вывод: если в задаче надо что-то сделать «для каждого делителя», это дёшево. Дорого — найти сами делители, и вот тут помогает решето с минимальным простым.
Сумма делителей
Та же логика даёт и сумму:
Каждая скобка — геометрическая прогрессия, и её можно свернуть в .
Делители всех чисел сразу
Если нужны делители каждого числа до , перебирать каждое до корня — это . Есть способ лучше: пройтись по каждому возможному делителю и отметить его кратные.
vector<vector<int>> divisorsOf(n + 1);
for (int d = 1; d <= n; d++)
for (int m = d; m <= n; m += d)
divisorsOf[m].push_back(d);
Работы здесь — то самое гармоническое число. Для это около 14 миллионов операций.
Тот же приём в упрощённом виде считает, например, количество делителей у каждого числа: вместо списка достаточно счётчика.
Взаимная простота и функция Эйлера
Сколько чисел от 1 до взаимно просты с ? Ответ даёт функция Эйлера:
Произведение берётся по различным простым делителям. Формула — прямое следствие включения-исключения: из всех чисел выбрасываем кратные каждому простому делителю, возвращаем кратные парам и так далее.
Считается она за то же время, что и разложение, а через решето — сразу для всех чисел до .