【刷题】数学知识——约数:求约数个数

在这里插入图片描述
假设一个数n可以拆成 n = p 1 a 1 ∗ p 2 a 2 ∗ . . . ∗ p k a k n=p_1^{a_1} * p_2^{a_2} *...*p_k^{a_k} n=p1a1p2a2...pkak
其中p是质数,如 36 = 2 2 ∗ 3 2 36=2^2*3^2 36=2232,
那么约数的个数就有 ( a 1 + 1 ) ∗ ( a 2 + 1 ) ∗ . . . ∗ ( a k + 1 ) (a_1+1)*(a_2+1)*...*(a_k+1) (a1+1)(a2+1)...(ak+1)个,即将是质数的约数个数+1后相乘
如36的约数个数就有 ( 2 + 1 ) ∗ ( 2 + 1 ) = 9 (2+1)*(2+1)=9 (2+1)(2+1)=9个:1 2 3 4 6 9 12 18 36。

这是因为每个约数必然是由几个质数组合而成,不同的约数所选的质数组合不一样。

而可选的质数只有 a 1 a_1 a1 p 1 p_1 p1 a 2 a_2 a2 p 2 p_2 p2,…, a k a_k ak p k p_k pk
问题转化成:从这些数中,共有多少种不同的选法。

因为第i个质数能选0~ a i a_i ai次,有 a i + 1 a_i+1 ai+1种选择,那么所有k个质数的选法就是 ( a 1 + 1 ) ∗ ( a 2 + 1 ) ∗ . . . ∗ ( a k + 1 ) (a_1+1)*(a_2+1)*...*(a_k+1) (a1+1)(a2+1)...(ak+1)

所以只要用试除法统计所有数的所有质数个数,将各个数的质数个数相加得到 a 1 , . . . , a k a_1,...,a_k a1,...,ak,再用 ( a 1 + 1 ) ∗ ( a 2 + 1 ) ∗ . . . ∗ ( a k + 1 ) (a_1+1)*(a_2+1)*...*(a_k+1) (a1+1)(a2+1)...(ak+1)相乘,就能得到答案。

PS. int范围内约数个数最多的数,约数有1500个左右。

#include <iostream>
#include <unordered_map> 
using namespace std;

long long ans = 1;
const int mod = 1e9 + 7;

int n, a;

unordered_map<int, int> primes;

int main() {
	scanf("%d", &n);
	while (n -- ) {
		scanf("%d", &a);
		
		for (int i = 2; i * i <= a; i ++ ) {
			while (a % i == 0) {
				a /= i;
				primes[i] ++;  // 各个数的约数个数相加 
			}
		}
		if (a > 1) primes[a] ++; // a是质数时 
	}
	for (auto prime: primes) {
		ans = ans * (prime.second + 1) % mod;
	}
	printf("%lld\n", ans); 
	return 0;
}

这边和试除法有些不同:
1、没有同时把 i i i a / i a/i a/i统计进去,这是因为这题只要统计是质数的约数。
2、为了确保把最大的质数统计进去,在结尾加了if (a > 1) primes[a] ++
105 = 3 ∗ 5 ∗ 7 105=3*5*7 105=357,只会在前面的循环中统计3和5,此时a=7, 7 < 5 \sqrt7<5 7 <5,退出循环,不会记录下7,因此要加上这句。
至于为什么至多只有一个质数没在循环中统计进去,这是因为枚举到 a = i ∗ i a=i*i a=ii时, i + 1 i+1 i+1 a a a至多只有1个质数是a的约数。如果有两个质数是a的约数,那么这两个质数相乘>a,矛盾。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值