从 s 开始枚举,用试除法求真约数和,找到第一对互为真约数和的友好数。
OJ: luogu
题目 ID: P1851
难度:普及-
标签:数学枚举
日期: 2026-06-18 20:35
题意
给出一个整数 s,要求找出第一对友好数 (A, B),满足较小的那个数不小于 s。
这里的友好关系定义为:
A的真约数和等于BB的真约数和等于AA != B
思路
先看一个可以直接验证想法的朴素解:
从 s 开始枚举每个 A,直接枚举 1..A-1 求真约数和,得到 B,再检查 sum(B) 是否回到 A。
cpp
#include <bits/stdc++.h>
using namespace std;
int sum_proper_divisors(int x) {
int sum = 0;
for (int d = 1; d < x; d++) {
if (x % d == 0) sum += d;
}
return sum;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int s;
cin >> s;
for (int a = s;; a++) {
int b = sum_proper_divisors(a);
if (b <= a) continue;
if (sum_proper_divisors(b) == a) {
cout << a << ' ' << b << '\n';
return 0;
}
}
return 0;
}这个做法的瓶颈在于求真约数和太慢。
正式做法利用约数成对出现的性质,只枚举到 sqrt(x),就能在
为了避免同一对数重复输出,只保留 A < B 的情况。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
int sum_proper_divisors(int x) {
if (x == 1) return 0;
int sum = 1;
for (int d = 2; 1LL * d * d <= x; d++) {
if (x % d) continue;
sum += d;
if (d * d != x) sum += x / d;
}
return sum;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int s;
cin >> s;
for (int a = s;; a++) {
int b = sum_proper_divisors(a);
if (b <= a) continue;
if (sum_proper_divisors(b) == a) {
cout << a << ' ' << b << '\n';
return 0;
}
}
return 0;
}复杂度
一次求真约数和的复杂度是 s 开始向上搜索到答案前,需要做若干次这样的判断。
空间复杂度是
总结
这题本质上是“枚举 + 真约数和判定”。 关键在于用平方根试除更快地求出约数和。