题目描述
输入n个整数,依次输出每个数的约数的个数
输入描述:
输入的第一行为N,即数组的个数(N<=1000) 接下来的1行包括N个整数,其中每个数的范围为(1<=Num<=1000000000) 当N=0时输入结束。
输出描述:
可能有多组输入数据,对于每组输入数据, 输出N行,其中每一行对应上面的一个数的约数的个数。
示例1
输入
5 1 3 4 6 12
输出
1 2 3 4 6
解题思路:
确定一个数约数的个数,最简单的方法就是暴力求解,遍历1到该数字,时间复杂度为O(n)。考虑到题中数字的范围为10亿,显然会超时。
联系数学知识,考虑对数字分解质因数,若n = a1b1a2b2a3b3a4b4...ambm(n != 1,其中a1,a2...am均为质数),则约数的个数为∏(bi+1) (i = 1,,2,3...m)。
接下来考虑如何分解质因数。显然暴力的方法也是不行的。一个可行的方法是找出某个范围内的质数,逐个分析,看能否整除n。若不能,尝试下一个质数,若能,记录该质数的bm。
范围的上界应当不小于10亿的平方根。若存在一个大于10亿平方根的数可以整除n,则这个数必然等于n除以小于该数的所有质数的结果,且该数的指数只能为1,否则其平方大于10亿。也就是说,若a大于n的平方根,则其指数只能为1。且最多存在一个这样的a。
接下来考虑如何找到该范围内的所有素数。这里有几种方法,确定一个数是否为素数,暴力的话复杂度是O(n),稍微优化之后是O(sqrt(n))。确定该范围内的所有素数,则时间复杂度为O(n * sqrt(n)),时间复杂度高。
考虑到是找一个范围内的所有素数,且素数的性质(不能被除1和它本身之外的其他数整除),那么当确定一个数为素数时,其倍数都不是素数。由此,可以遍历该范围,每次找到一个素数时,标记其k次方的数为非素数。此算法时间复杂度为O(n * loglogn)。时间复杂度证明参考:https://blog.csdn.net/zzxian/article/details/8263091
代码:
#include <iostream> #include <vector> #include <cmath> using namespace std; bool flag[40001] = {0}; vector<int> zhi; void shai() { for(int i = 2;i <= 40000;i++) { if(!flag[i]) { zhi.push_back(i); for(long long j = i * i;j <= 40000;j *= i) { // cout << j << endl; flag[j] = 1; } } } } int yue(int num) { vector<int> v; int ans = 1; int cnt = 0; int zhi_size = (int)zhi.size(); for(int i = 0;i < zhi_size;) { int temp = zhi[i]; if(num < temp) break; if(num % temp == 0) { cnt++; num /= temp; } else { v.push_back(cnt + 1); cnt = 0; i++; } } if(cnt != 0) v.push_back(cnt + 1); if(num != 1) v.push_back(2); int size = v.size(); for(int i = 0;i < size;i++) { ans *= v[i]; } return ans; } int main() { int n; shai(); while(cin >> n) { if(n == 0) break; else { for(int i = 0;i < n;i++) { int num; cin >> num; cout << yue(num) << endl; } } } return 0; }