约数相关知识点详解
本篇随笔讲解信息学奥林匹克竞赛中数学部分的约数相关知识点。大体包括:整数唯一分解定理的推论,求(N)的正约数集合,筛选(1 - N)每个数的正约数集合。需要读者有不低于高中一年级的数学素养及一定的逻辑推理能力。本篇随笔将不再对一些基本定理和数学知识、概念进行讲解,有需要的同学请自行补习。
-
整数唯一分解定理的推论
通过质数相关知识点的学习,我们知道了一个正整数可以被分解成若干质数的乘积,数学表示为:
将其展开,可以得到:
通过约数的定义,我们可知这个展开式中的所有元素都是(N)的约数。
那么,根据乘法原理,(N)的约数个数(T)就可以表示为:
即:
这就是正整数的正约数个数表达式。
那么,还是根据正整数的唯一分解定理,(N)的所有正约数的和(S)就是:
即:
这就是正整数的所有约数和表达式。
-
求(N)的正约数集合
我们知道了一个正整数的约数和和约数个数,但是我们还是不能知道这个正整数的约数到底是什么。想要知道具体的约数,需要枚举实现。
但是约数有一个性质:它是成对出现的(完全平方数除外)。
假如一个正整数(a)是(N)的约数,且(alesqrt n),那么必然会有一个正整数(b)是(N)的约数,且(bgesqrt n)。
而且根据约数的性质,我们完全可以断定,这个(b)就是(frac{N}{a})。
因此,和质数的判定对应地,我们使用试除法。同样只需要枚举(1 - sqrt N)的所有数,每枚举到一个可被(N)整除的数,就把这个数和(N)除这个数所得的数一起加入到约数集合中。
当然,在细节实现上要判一下它是不是完全平方数。
代码:
void factor(int n)
{
for(int i=1;i<=sqrt(n);i++)
if(n%i==0)
{
f[++cnt]=i;
if(i!=n/i)
f[++cnt]=n/i;
}
}
并且,通过试除法的原理,我们还可以推导出:
一个数的约数个数的上界为(2sqrt N)
-
筛选(1-N)每个数的正约数集合
如果用上述方法进行求解,时间复杂度为(O(Nsqrt N)),会被卡。根据约数的定义,我们可以推出:对于每个数(d),这个(1 -N)的数列中以之为约数的数一定是它的倍数。即(d,2d,3d,4dcdotslfloor N/d floor imes d)。
所以,我们每枚举到一个数的时候,就直接把它的上述倍数加进表格即可。
代码:
vector<int> f[maxn];
void factors(int n)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n/i;j++)
f[i*j].push_back(i);
}
上述算法的时间复杂度是(O(NlogN))级别的。