Редакция для Совершенное число
Submitting an official solution before solving the problem yourself is a bannable offence.
Автор:
1. Идея
Нужно проверить, равно ли число n сумме всех своих истинных делителей, то есть всех положительных делителей, кроме самого n.
Наивный способ — перебрать все числа от 1 до n - 1 и проверить, делят ли они n. Но при n до 10^12 так делать нельзя.
Главная идея — перебирать делители не до n, а только до sqrt(n). Если d делит n, то вместе с ним сразу находится парный делитель n / d.
Например, если n = 28, то:
2— делитель, парный к нему144— делитель, парный к нему7
Так можно быстро найти все делители и посчитать сумму истинных делителей.
2. Наблюдения
У любого делителя
d, меньшего корня изn, есть парный делительn / d, больший корня изn.Поэтому достаточно проверить все
dот2до тех пор, покаd * d <= n.Делитель
1всегда является истинным делителем любого числа, кроме случаяn = 1.- Для
n = 1истинных делителей вообще нет, их сумма равна0, значит ответNO. - Для всех
n > 1можно сразу начать сумму с1.
- Для
Если
d * d == n, то делитель встречается в паре сам с собой.- Например, у
36делитель6парный сам себе. - В таком случае его нужно прибавить только один раз.
- Например, у
3. Алгоритм
- Считать
n. - Если
n == 1, вывестиNO. - Иначе завести сумму
sum = 1, потому что1— истинный делитель любого числа больше1. - Перебирать
dот2, покаd * d <= n:- если
n % d == 0, то:- прибавить
dк сумме; - найти
other = n / d; - если
other != d, тоже прибавитьother.
- прибавить
- если
- После перебора сравнить
sumиn:- если
sum == n, вывестиYES; - иначе вывести
NO.
- если
4. Почему это работает
Докажем, что алгоритм действительно считает сумму всех истинных делителей.
Для числа n > 1:
- делитель
1мы добавляем сразу; - дальше рассматриваем все числа
dот2доsqrt(n).
Если d делит n, то существует парный делитель other = n / d.
Тогда:
d— делитель числаn;other— тоже делитель числаn.
Все делители числа, кроме 1 и самого n, обязательно попадут в одну из таких пар:
- либо как маленький делитель
d <= sqrt(n), - либо как большой делитель
n / d.
Значит, перебирая все d до корня, мы находим все делители.
Почему не возникает пропусков:
- если делитель меньше корня, мы встретим его напрямую;
- если делитель больше корня, то его парный делитель меньше корня, и через него мы тоже его найдём.
Почему не возникает лишних добавлений:
- если
dиn / dразличны, это два разных делителя, оба нужно добавить; - если
d * d == n, тоd = n / d, и этот делитель один, поэтому добавляем его только один раз.
Таким образом, сумма получается ровно суммой всех истинных делителей числа n. После этого остаётся только проверить, равна ли она самому числу.
5. Сложность
Перебор идёт только до sqrt(n).
- Время:
O(sqrt(n)) - Память:
O(1)
При n <= 10^12 это подходит: максимум около 10^6 проверок.
6. Код на C++17
#include <iostream>
using namespace std;
int main() {
long long n;
cin >> n;
if (n == 1) {
cout << "NO\n";
return 0;
}
long long sum = 1;
for (long long d = 2; d * d <= n; d++) {
if (n % d == 0) {
sum += d;
long long other = n / d;
if (other != d) {
sum += other;
}
}
}
if (sum == n) {
cout << "YES\n";
} else {
cout << "NO\n";
}
return 0;
}
7. Код на Python 3
n = int(input())
if n == 1:
print("NO")
else:
s = 1
d = 2
while d * d <= n:
if n % d == 0:
s += d
other = n // d
if other != d:
s += other
d += 1
if s == n:
print("YES")
else:
print("NO")
Комментарии