【算法专题】约数个数

约数个数

约数个数的普通求法

首先我们根据数的唯一分解定理,对 \(N\) 进行分解得:

\[N = p_1^{c_1} \times p_2^{c_2} \times p_3^{c_3} \times ... \times p_k^{c_k} \]

由约数的定义: \(p_1^{c_1}=p_1^{0} \times p_1^{1} \times p_1^{2} \times ... \times p_1^{c_1}\) 共有 \(c_1+1\) 项。

由此类推,那么 \(N\) 的约数个数由乘法原理可求得:

\[(c_1+1)(c_2+1)(c_3+1)...(c_k+1) \]

所以我们在对 \(N\) 分解质因数的时候可以很容易的求出每个 \(p_i\) 的指数,根据公式就可得到 \(N\) 的约数个数。

unordered_map<int, int> mp; // 存储分解后的结果
void devide(int n) // 对n分解质因数
{
	for (int i = 2; i <= n / i; i++) // 分解到根号n就行了
	{
		while (n % i == 0)
		{
			mp[i]++;
			n /= i;
		}
	}
	if (n > 1) mp[n]++;
}
int calc() // 计算约数个数
{
	int sum = 1;
	for (auto t : mp) sum *= (t.second + 1);
	return sum;
}

显而易见,该算法的时间复杂度为 \(O(\sqrt n)\),那么假如我们求 [1,n] 中的所有数的约数个数,那么时间复杂度显而易见的变成了 \(O(n \sqrt n)\)

约数个数的线性求法

在之前的素数筛法中,我们有一个筛法是线性的,就是欧拉筛。那么我们有没有可能在欧拉筛的同时直接求出来每个数的约数的个数呢?这是有可能的。

首先需要准备两个数组:

  • d[i]:表示 \(i\) 的约数个数
  • num[i]:表示 \(i\) 的最小质因子个数,就相当于 \(p_1\) 的指数

有了两个数组了,我们首先分情况讨论:

(一)、\(i\) 为质数:

\[\begin{cases} \ \ \ d[i]=2\\ num[i]=1 \end{cases} \]

  • d[i]\(i\) 是质数,所以约数只有 \(1\)\(i\) 两个
  • num[i]\(i\) 是质数,所以最小质因子是 \(i\) ,指数为 \(1\)

(二)、\(primes[j]\) 整除 \(i\)

我们既然是递推求 d[i],那么我们就是要 d[i * primes[j]d[i] 推过来。

首先我们知道:

  • \(primes[j]\)\(i\) 的质因子,那么 \(i*primes[j]\) 唯一分解出来不会产生新的质因子,依旧是 \(p_1,p_2,...,p_k\)
  • \(primes[j]\) 是从小到大枚举的,那么 \(primes[j]\)\(i\) 的最小质因子,所以 \(p_1\) 的指数要加上 \(1\)

\[d[i*primes[j]]=(1+c_1+1)(c_2+1)(c_3+1)...(c_k+1) \]

那么怎么和 \(d[i]\) 关联上,这里就需要用到 \(num[i]\) 了,根据之前的推导 num[i*primes[j]]=num[i]+1 ,因为 \(i*primes[j]\) 这个数只是 \(i\) 的最小质因子的指数多了 \(1\) ,所以我们直接加 \(1\) 即可。

\[d[i] = (num[i]+1)(c_2+1)(c_3+1)...(c_k+1)\\ \\ d[i*primes[j]] = (num[i]+2)(c_2+1)(c_3+1)...(c_k+1)\\ \\ d[i*primes[j]] = d[i] / (num[i]+1) \times (num[i]+2) \]

(三)、\(primes[j]\) 不整除 \(i\)

因为不能整除了,所以 \(i * primes[j]\) 这个数一定多了一个质因子,就是 \(primes[j]\)

\[d[i*primes[j]]=(c_1+1)(c_2+1)(c_3+1)...(c_k+1)(1+1)\\\\ =d[i]\times 2=d[i]\times d[primes[j]] \]

对于 i*primes[j] 这个数的最小质因子,因为我们是从小的质因子开始枚举的,那么 \(primes[j]\) 一定是最小质因子且它的个数只有一个,所以 \(num[i*primes[j]]=1\)

综上,我们可以写出完整代码了!

int primes[N], cnt; // 存质数
bool st[N]; 
int d[N], num[N]; // d:约数个数数组,num:最小质因子个数数组
void init(int n)
{
    for (int i = 2; i <= n; i++)
    {
        if (!st[i]) primes[cnt++] = i, d[i] = 2, num[i] = 1;
        for (int j = 0; primes[j] * i <= n; j++)
        {
            st[i * primes[j]] = true;
            if (i % primes[j] == 0)
            {
                d[i * primes[j]] = d[i] / (num[i] + 1) * (num[i] + 2);
                num[i * primes[j]] = num[i] + 1;
                break;
            }
            else
            {
                d[i * primes[j]] = d[i] * 2;
                num[i * primes[j]] = 1;
            }
        }
    }
}
posted @ 2024-02-05 22:25  高明y  阅读(451)  评论(0)    收藏  举报