题目理解
核心要求:给定多个正整数,计算并输出每个数的约数(因数)个数。
输入格式:
- 第一行:正整数N(1 ≤ N ≤ 1000),表示需要处理的数字个数
- 第二行:N个正整数,每个数的范围是1 ≤ Num ≤ 10^9
输出格式:
- 对于每组输入数据,输出N行
- 每行对应输入中相应位置数字的约数个数
关键约束条件:
- 数字范围较大(最大10^9),需要高效的算法
- 可能存在多组测试数据
- 需要处理完全平方数的特殊情况
解题思路
直觉思路(暴力方法)
最直接的想法是遍历从1到x的所有数字,检查是否能整除:
int count = 0;
for(int i = 1; i <= x; i++) {
if(x % i == 0) count++;
}
问题:当x = 10^9时,需要循环10^9次,时间复杂度O(x),会超时。
优化思路
核心观察:约数是成对出现的。如果i是x的约数,那么x/i也是x的约数。
例如:24的约数有1, 2, 3, 4, 6, 8, 12, 24
- 1 × 24 = 24
- 2 × 12 = 24
- 3 × 8 = 24
- 4 × 6 = 24
优化策略:只需要遍历到√x即可,因为超过√x的约数都可以通过配对得到。
最终解法
- 遍历i从1到√x
- 如果i能整除x,说明i和x/i都是约数,计数+2
- 特殊处理:如果x是完全平方数(√x × √x = x),那么√x只算一个约数
正确性证明:
- 对于任意约数d,必然存在配对约数x/d
- 当d < √x时,x/d > √x,不会重复计数
- 当d = √x时,d = x/d,只能计数一次
- 当d > √x时,已经在前面的循环中被x/d计数过了
算法设计
使用的算法:数学优化 + 枚举
关键步骤:
- 计算x的平方根:
int n = sqrt(x) - 遍历1到√x-1(注意边界)
- 对每个能整除的i,计数+2
- 检查是否为完全平方数,如果是则计数+1
数据结构:无需特殊数据结构,使用基本变量即可
代码解析
ans函数详解
int ans(int x){
int count=0; // 初始化约数计数器
int n=sqrt(x); // 计算x的平方根,用于优化循环次数
// 遍历从1到√x-1(注意i*i < x,不包含等于的情况)
for(int i=1; i*i < x; i++){
if(x % i == 0){ // 如果i能整除x
count += 2; // i和x/i都是约数,计数+2
}
}
// 特殊处理完全平方数
if(n * n == x){ // 如果x是完全平方数
count++; // √x只能算一个约数
}
return count;
}
难点和易错点:
-
循环边界 :
i*i < x而不是i <= sqrt(x)- 使用
i*i < x避免了浮点数精度问题 - 如果使用
i <= sqrt(x),浮点数比较可能产生误差
- 使用
-
完全平方数处理:
- 必须单独处理,否则会漏掉√x这个约数
- 例如:16的约数是1,2,4,8,16,其中4只出现一次
-
计数逻辑:
- 每次找到一个约数i,同时找到配对约数x/i
- 但完全平方数的√x不能重复计数
main函数详解
int main() {
int n;
while (cin >> n) { // 处理多组测试数据
while (n--) { // 对每组数据中的每个数字
int x;
cin >> x;
cout << ans(x) << endl; // 输出约数个数
}
}
return 0;
}
设计要点:
- 使用
while(cin >> n)处理多组输入 - 内层循环处理每组数据中的所有数字
- 对每个数字调用ans函数计算约数个数
复杂度分析
时间复杂度
单个数字的计算:O(√x)
- 循环次数最多为√x次
- 当x = 10^9时,√x = 31622,远小于10^9
整体复杂度:O(N × √x_max)
- N为数字个数(≤ 1000)
- x_max为最大数字(≤ 10^9)
- 最坏情况下:1000 × 31622 ≈ 3.16 × 10^7次操作,可接受
空间复杂度
O(1):只使用了常数个变量,无需额外存储空间
优化与扩展
可能的优化
-
质因数分解法(更优解)
- 如果x = p₁^a₁ × p₂^a₂ × ... × pₖ^aₖ
- 约数个数 = (a₁+1) × (a₂+1) × ... × (aₖ+1)
- 时间复杂度:O(√x)用于质因数分解,但常数更小
- 适合多次查询相同数字的场景
-
预处理优化
- 如果数字范围较小,可以预处理所有数字的约数个数
- 使用筛法思想,时间复杂度O(N log N)
推广应用
- 判断完全数:如果约数和等于2x(包括自身)
- 判断亲和数:两个数的约数和相等
- 计算约数和:类似思路,累加约数而非计数
- 数论问题:欧拉函数、莫比乌斯函数等
边界情况测试
// 测试用例
ans(1) = 1 // 只有1本身
ans(2) = 2 // 1, 2
ans(4) = 3 // 1, 2, 4(完全平方数)
ans(12) = 6 // 1, 2, 3, 4, 6, 12
ans(100) = 9 // 1, 2, 4, 5, 10, 20, 25, 50, 100
总结
本题通过数学观察(约数成对出现)将时间复杂度从O(x)优化到O(√x),是典型的数学优化问题。关键在于:
- 理解约数的对称性:利用i和x/i的配对关系
- 处理边界情况:完全平方数的特殊处理
- 避免精度问题:使用i*i < x而非浮点数比较
这种思路在数论问题中非常常见,掌握后可以解决很多类似的约数相关问题。