Bootstrap

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

在这里插入图片描述
假设一个数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,

那么约数的和就是 ( p 1 0 + p 1 1 + . . . + p 1 a 1 ) ∗ ( p 2 0 + p 2 1 + . . . + p 2 a 2 ) ∗ . . . ∗ ( p k 0 + p k 1 + . . . + p k a k ) (p_1^0+p_1^1+...+p_1^{a_1})*(p_2^0+p_2^1+...+p_2^{a_2})*...*(p_k^0+p_k^1+...+p_k^{a_k}) (p10+p11+...+p1a1)(p20+p21+...+p2a2)...(pk0+pk1+...+pkak)

如36的约数之和就是 ( 1 + 2 + 2 2 ) ∗ ( 1 + 3 + 3 2 ) = 91 = 1 + 2 + 3 + 4 + 6 + 9 + 12 + 18 + 36 (1+2+2^2)*(1+3+3^2) = 91=1+2+3+4+6+9+12+18+36 (1+2+22)(1+3+32)=91=1+2+3+4+6+9+12+18+36


因为所有约数都是由若干个这样的质数p组成的,而上面的乘法展开就是所有质数的组合。

以36为例: ( 1 + 2 + 2 2 ) ∗ ( 1 + 3 + 3 2 ) (1+2+2^2)*(1+3+3^2) (1+2+22)(1+3+32)
1 = 1 ∗ 1 1=1*1 1=11
2 = 2 ∗ 1 2=2*1 2=21

12 = 2 2 ∗ 3 12=2^2*3 12=223
18 = 2 ∗ 3 2 18=2*3^2 18=232
36 = 2 2 ∗ 3 2 36=2^2*3^2 36=2232


用试除法统计所有数的所有质数个数 a i a_i ai,用式子 ( p 1 0 + p 1 1 + . . . + p 1 a 1 ) ∗ ( p 2 0 + p 2 1 + . . . + p 2 a 2 ) ∗ . . . ∗ ( p k 0 + p k 1 + . . . + p k a k ) (p_1^0+p_1^1+...+p_1^{a_1})*(p_2^0+p_2^1+...+p_2^{a_2})*...*(p_k^0+p_k^1+...+p_k^{a_k}) (p10+p11+...+p1a1)(p20+p21+...+p2a2)...(pk0+pk1+...+pkak)得到答案。

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

long long cnt, 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) {
		//printf("%d %d\n", prime.first, prime.second); 
		long long p = 1;
		cnt = 0;
		for (int i = 0; i <= prime.second; i ++ ) {
			cnt = (cnt + p) % mod;
			p = (p * prime.first) % mod; //计算p的1,2,3...次方
		}
		ans = (ans * cnt) % mod;
	}
	printf("%lld\n", ans); 
	return 0;
}

注意这边的取模,由模运算的规则
( a + b ) % p = ( a % p + b % p ) % p (a + b) \% p = (a \% p + b \% p) \% p (a+b)%p=(a%p+b%p)%p
( a ∗ b ) % p = ( a % p ∗ b % p ) % p (a * b) \% p = (a \% p * b \% p) \% p (ab)%p=(a%pb%p)%p

所以上述代码的结果是等价于最后再取模的。

注意到
p 1 0 + p 1 1 + . . . + p 1 a 1 = ( ( p 1 + 1 ) ∗ p 1 + 1 ) ∗ p 1 + 1 ) ∗ p 1 + 1.... p_1^0+p_1^1+...+p_1^{a_1}=((p_1+1)*p_1+1)*p_1+1)*p_1+1.... p10+p11+...+p1a1=((p1+1)p1+1)p1+1)p1+1....
总共进行 a 1 a_1 a1
因此循环还可以改写成

	for (auto prime: primes) {
		cnt = 0;
		for (int i = 0; i <= prime.second; i ++ ) {
			cnt = (cnt * prime.first + 1) % mod; 
		}
		ans = (ans * cnt) % mod;
	}
;