求约数和(公式、原理以及Java代码实现)

一、例题

  • 给定n个正整数 ai,请你输出这些数的乘积的约数之和,答案对 1e9+7 取模
  • 第一行包含整数n
  • 接下来n行,每行包含一个整数
  • 输出一个整数,表示所给正整数的乘积的约数之和,答案需对 1e9+7 取模

样例输入:

3
2
6
8

样例输出:

252

二、原理与分析

(1)求约数之和的公式

p为底数,a为指数

(p1^0+p1^1+p1^2+...+p1^ak)*(p2^0+p2^1+p2^2+...+p2^ak)*...*(pk^0+pk^1+pk^2+...+pk^ak)
(2)公式推理
  • 此公式按照乘法分配率展开后是一堆乘积+一堆乘积+一堆乘积的形式
  • 每一堆乘积就对应一种约数,相加即为约数之和
(3)总体思路

(1)依次将输入的每一个数字分解质因数,并将其底数与指数存放在HashMap中

(2)然后对每一个底数与指数进行分解计算

  • 循环到一对底数p与指数a
  • 要求
    p1^0+p1^1+p1^2+...+p1^a
  • 则需要循环a次
  • 令求和的t初始值为1,每循环一次 *p+1
    初始:   t=1
    第一趟:  t=p+1
    第二趟:  t=(p+1)*p+1=p^2*p+1
    执行a次可得到 p^a+p^(a-1)+...+p^2+p+1
  • 有多少对底数与指数就将上述的过程再执行多少趟趟,就可得出所需求约数之和的式子
  • 例如:
   2*6*8= 96 = (2^5)*(3^1)
   然后对每一个进行分解计算
   (2^0+2^1+2^2+2^3+2^4+2^5)*(3^0+3^1)=252
(4)小知识
  • int范围内的整数,约数个数最多的大概是1500个,有时可以用来快速验证答案
(5)HashMap
  • 由于每一个p1和b1都是一一对应的,即底数和指数时一一对应的,我们可以用HashMap来存储
  • 因此需要了解Map集合的相关知识:
    Map集合是一种双列集合,每个元素包含两个数据。
    Map集合的每个元素的格式:key=value(键值对元素)。
    Map集合也被称为”键值对集合“
    键和值一一对应,一个键对应一个值,整个集合的特点是由键决定的
    HashMap:元素按照键是无序,不重复,无索引,值不做要求。(与Map体系一致)
    getOrDefault(key, default):如果存在key, 则返回其对应的value, 否则返回给定的默认值

更加详细的关于HashMap的原理与使用可以看我的另一篇博客
暑期JAVA学习(13)Map集合体系

三、代码与注释

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

//题目:
//给定n个正整数 ai,请你输出这些数的乘积的约数之和,答案对 1e9+7 取模
//第一行包含整数 n
//接下来 n 行,每行包含一个整数 ai
//输出一个整数,表示所给正整数的乘积的约数之和,答案需对 1e9+7 取模

//求约数之和的公式:(p1^0+p1^1+p1^2+...+p1^ak)*(p2^0+p2^1+p2^2+...+p2^ak)*...*(pk^0+pk^1+pk^2+...+pk^ak)
//此公式按照乘法分配率展开后是一堆乘积+一堆乘积+一堆乘积的形式
//每一堆乘积就对应一种约数

//总体思路:
//(1)依次将输入的每一个数字分解质因数,并将其底数与指数存放在HashMap中
//(2)然后对每一个底数与指数进行分解计算
//    循环到一对底数p与指数a
//    要求p1^0+p1^1+p1^2+...+p1^a
//    循环a次
//    令求和的t初始值为1,每循环一次 *p+1
//    初始:   t=1
//    第一趟:  t=p+1
//    第二趟:  t=(p+1)*p+1=p^2*p+1
//    执行a次可得到 p^a+p^(a-1)+...+p^2+p+1
//    有多少对底数与指数就将上述的过程再执行多少趟趟,就可得出所需求约数之和的式子
//例如:
//2*6*8= 96 = (2^5)*(3^1)
//然后对每一个进行分解计算
//(2^0+2^1+2^2+2^3+2^4+2^5)*(3^0+3^1)=252

public class Main {
    public static long mod = 1000000007;
    //定义一个HashMap来对应存储所有项的底数与指数,一一对应,方便使用
    public static HashMap<Integer, Integer> primes = new HashMap<>();

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();//一共有n个正整数
        //将所有的数分解质因数,并存放在primes中
        //遍历每一个输入的整数,总共遍历n次
        while (n-- > 0){
            int x = sc.nextInt();
            //每个合数都可以写成几个质数相乘的形式,这几个质数就都叫做这个合数的质因数
            //此处用到的就是分解质因数的算法
            //用i遍历x的底数,若i为x的底数(即x%i==0),就把i对应的指数加一,即把map的value值加一
            //例如8=2^3,那么map中key为2的value值就一共会加3
            for (int i = 2; i <= x/i; i++) {
                while (x % i == 0){
                    x = x/i;
                    //getOrDefault(key, default)如果存在key, 则返回其对应的value, 否则返回给定的默认值
                    primes.put(i, primes.getOrDefault(i, 0) + 1);//这一句用getOrDefault是防止NullPointerException
                }
            }
            //如果说x被所有小于根号x的质数因子除完后,还大于1,说明剩下的一定是那个大于根号x的质因子,直接输出即可 (例如11,43这样的数)
            //证明:一个数x中最多包含一个大于根号x的质数因子,很好证明,如果有两个大于根号x的质数因子那么这俩相乘就大于x了,反证法成立
            if (x > 1){
                primes.put(x, primes.getOrDefault(x, 0) + 1);
            }
        }
        //result存储最终结果
        long result = 1;
        //遍历Map
        //Map.entrySet() 这个方法返回的是一个Set<Map.Entry<K,V>>
        //Map.Entry 是Map中的一个接口,他的用途是表示一个映射项(里面有Key和Value)
        //而Set<Map.Entry<K,V>>表示一个映射项的Set。Map.Entry里有相应的getKey和getValue方法
        for (Map.Entry<Integer,Integer> i : primes.entrySet()) {
            long t = 1;
            int p = i.getKey();//p是底数
            int a = i.getValue();//a是次方
            while (a-- > 0){
                t = (t*p+1)%mod;
            }
            result = result * t % mod;
        }
        System.out.println(result);
    }
}

在这里插入图片描述
纯代码

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main {
    public static long mod = 1000000007;
    public static HashMap<Integer, Integer> primes = new HashMap<>();

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        while (n-- > 0){
            int x = sc.nextInt();
            for (int i = 2; i <= x/i; i++) {
                while (x % i == 0){
                    x = x/i;
                    primes.put(i, primes.getOrDefault(i, 0) + 1);
                }
            }
            if (x > 1){
                primes.put(x, primes.getOrDefault(x, 0) + 1);
            }
        }
        long result = 1;
        for (Map.Entry<Integer,Integer> i : primes.entrySet()) {
            long t = 1;
            int p = i.getKey();//p是底数
            int a = i.getValue();//a是指数
            while (a-- > 0){
                t = (t*p+1)%mod;
            }
            result = result * t % mod;
        }
        System.out.println(result);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呦呦呦欸哟哟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值