当前位置: 首页 > article >正文

数据结构与算法学习笔记----约数

数据结构与算法学习笔记----约数

@@ author: 明月清了个风
@@ first publish time: 2024.12.30

ps⭐️主要是求约数,约数的个数,约数的和,涉及到算术基本定理的相关内容,第三题的讲解合并在第二题的思路里一起了。

Acwing 869. 试除法求约数

[原题链接](869. 试除法求约数 - AcWing题库)

给定 n n n个正整数 a i a_i ai,对于每个整数 a i a_i ai,请你按照从小到大的顺序输出它的所有约数

输入格式

第一行包含整数 n n n

接下来 n n n行,每行包含一个正整数 a i a_i ai

输出格式

输出 n n n行,其中每 i i i行输出第 i i i个整数 a i a_i ai的所有约数。

数据范围

1 ≤ n ≤ 100 1 \le n \le 100 1n100,

1 ≤ a i ≤ 2 × 1 0 9 1 \le a_i \le 2\times 10^9 1ai2×109

思路

试除法和质数中是一样的,只是我们统计的量不一样了

需要注意的就是时间复杂度了,数论中的题目需要注意时间复杂度,这关系到我们要使用什么方法解决

这题的时间复杂度是 O ( n × a ) O(n \times \sqrt{a}) O(n×a ) a a a的范围如题,因此可以得到整体时间复杂度大概在400万~500万。

代码

#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> get_dividers(int n)
{
    vector<int> res;
    for(int i = 1; i <= n / i; i ++)
    {
        if(n % i == 0)
        {
            res.push_back(i);
            if(i != n / i) res.push_back(n / i);
        }
    }
    sort(res.begin(), res.end());
    return res;
}

int main()
{
    int n;
    cin >> n;
    
    while(n --)
    {
        int x;
        cin >> x;
        vector<int> ans = get_dividers(x);
        
        for(auto t : ans) cout << t << ' ';
        cout << endl;
    }
    
    return 0;    
}

Acwing 870. 约数个数

[原题链接](870. 约数个数 - AcWing题库)

给定 n n n个正整数 a i a_i ai,请你输出这些数的乘积的约数个数,答案对 1 0 9 + 7 10^9 + 7 109+7取模。

输入格式

第一行包含整数 n n n

接下来 n n n行,每行包含一个正整数 a i a_i ai

输出格式

输出一个整数,表示所给正整数的乘积的约数个数,答案需对 1 0 9 + 7 10^9 + 7 109+7取模。

数据范围

1 ≤ n ≤ 100 1 \le n \le 100 1n100,

2 ≤ a i ≤ 2 × 1 0 9 2 \le a_i \le 2 \times 10^9 2ai2×109

思路

这里就要用到算术基本定理(这个定理的相关内容从下面的红字开始,若想跳过直接往下看蓝字)了。

算术基本定理,又称为正整数的唯一分解定理,是初等数论中一个非常重要的定理。它揭示了整数的基本性质,具体表述为:

任何一个大于1的自然数,如果不为质数,那么它可以唯一分解成有限个质数的乘积。

数学上,这可以表达为:

N = p 1 a 1 × p 2 a 2 × p 3 a 3 × ⋯ × p n a n N = p_1^{a_1} \times p_2^{a_2} \times p_3^{a_3} \times \cdots \times p_n^{a_n} N=p1a1×p2a2×p3a3××pnan

其中, p 1 < p 2 < p 3 < ⋯ < p n p_1 < p_2 < p_3 < \cdots < p_n p1<p2<p3<<pn 均为质数, a 1 , a 2 , a 3 , … , a n a_1, a_2, a_3, \ldots, a_n a1,a2,a3,,an 是正整数。这种分解称为 N N N 的标准分解式。

算术基本定理包含两个核心要点

  1. 存在性:每个大于1的自然数都可以写成质数的乘积。
  2. 唯一性:这种分解方式是唯一的,即不考虑质数的排列顺序,每个大于1的自然数只有一种质因数分解方式。

例如

  • 数字 60 60 60 可以唯一分解为 2 2 × 3 × 5 2^2 \times 3 \times 5 22×3×5
  • 数字 825 825 825 可以唯一分解为 3 × 5 2 × 11 3 \times 5^2 \times 11 3×52×11

证明方法简述

  1. 存在性证明

    • 假设存在某个大于1的自然数不能写成质数的乘积。
    • 选择所有这样的数中最小的那个,设为 n n n
    • 由于 n n n 不是质数,它可以分解为两个小于 n n n 的自然数的乘积。
    • 但这两个数都可以写成质数的乘积(因为它们都小于 n n n,且假设中不存在不能分解为质数乘积的数)。
    • 因此, n n n 也可以写成质数的乘积,与假设矛盾。
  2. 唯一性证明

    • 假设存在某个大于1的自然数可以以多于一种的方式写成质数的乘积。
    • 选择所有这样的数中最小的那个,设为 n n n
    • n n n 的两种分解式中的质数进行比较,利用反证法和欧几里得引理(若质数 p p p 能整除两数之积,则必能整除其中至少一个数)推导出矛盾。

上面是算术基本定理的相关内容,重新回到题目中,对于每个数 a i a_i ai,我们都可以进行这样的分解,那么每个数的约数的个数就可以看着所有质因数 p i a i p_i^{a_i} piai的排列组合,因此约数的个数就可以得到为 ( a 1 + 1 ) ( a 2 + 1 ) ⋯ ( a n + 1 ) (a_1 + 1)(a_2 + 1)\cdots(a_n + 1) (a1+1)(a2+1)(an+1)

这里还有个小知识点,int范围内约数个数最多的整数大概有1500个约数。

这里就直接将下一题的约数之和的算法讲了。

约数之和为 ( p 1 0 + p 1 1 + ⋯ + p 1 a 1 ) ⋯ ( p n 0 + p n 1 + ⋯ + p n a n ) ({p_1}^0+ {p_1}^1+ \cdots + {p_1}^{a_1}) \cdots ({p_n}^0+ {p_n}^1+ \cdots + {p_n}^{a_n}) (p10+p11++p1a1)(pn0+pn1++pnan),这应该也很好理解,这个多项式乘法的展开就是在每个括号中取一项乘起来,那么每个括号中的取法就有 ( a i + 1 ) (a_i + 1) (ai+1)种,所以最后一共有 ( a 1 + 1 ) ( a 2 + 1 ) ⋯ ( a n + 1 ) (a_1 + 1)(a_2 + 1)\cdots(a_n + 1) (a1+1)(a2+1)(an+1)项,即约数的个数,将这些项全部相加,即为约数之和。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <unordered_map>

using namespace std;

typedef long long LL;

const int mod = 1e9 + 7;

int main()
{
    int n;
    cin >> n;
    
    unordered_map<int, int> primes;
    while(n --)
    {
        int x; 
        cin >> x;
        
        for(int i = 2; i <= x / i; i ++)
        {
            while(x % i == 0)
            {
                x /= i;
                primes[i] ++;
            }
        }
        
        if(x > 1) primes[x] ++ ;
    }
    
    LL res = 1;
    for(auto prime : primes) res = res * (prime.second + 1) % mod;
    
    cout << res << endl;
    
    return 0;
}

Acwing 871. 约数之和

[原题链接](871. 约数之和 - AcWing题库)

给定 n n n个正整数 a i a_i ai,请你输出这些数的乘积的约数之和,答案对 1 0 9 + 7 10^9 + 7 109+7取模。

输入格式

第一行包含整数 n n n

接下来 n n n行,每行包含一个正整数 a i a_i ai

输出格式

输出一个整数,表示所给正整数的乘积的约数之和,答案需对 1 0 9 + 7 10^9 + 7 109+7取模。

数据范围

1 ≤ n ≤ 100 1 \le n \le 100 1n100,

2 ≤ a i ≤ 2 × 1 0 9 2 \le a_i \le 2 \times 10^9 2ai2×109

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <unordered_map>

using namespace std;

typedef long long LL;

const int mod = 1e9 + 7;

int main()
{
    int n;
    cin >> n;
    
    unordered_map<int, int> primes;
    while(n --)
    {
        int x; 
        cin >> x;
        
        for(int i = 2; i <= x / i; i ++)
        {
            while(x % i == 0)
            {
                x /= i;
                primes[i] ++;
            }
        }
        if(x > 1) primes[x] ++;
    }
    
    LL res = 1;
    for(auto prime : primes)
    {
        int a = prime.first, b = prime.second;
        LL t = 1;
        while(b --) t = (t * a + 1) % mod;
        res = res * t % mod;
    }
    
    cout << res << endl;
    
    return 0;
}

http://www.kler.cn/a/459312.html

相关文章:

  • Java 内存溢出(OOM)问题的排查与解决
  • 软考 高级 架构师 第十 章软件工程3
  • Unity3D仿星露谷物语开发16之角色拾取道具
  • 实现一个通用的树形结构构建工具
  • 我的桌面 1.9.75 | 个性化定制手机桌面,丰富的小组件和主题
  • 库伦值自动化功耗测试工具
  • 群落生态学研究进展▌Hmsc包对于群落生态学假说的解读、Hmsc包开展单物种和多物种分析的技术细节及Hmsc包的实际应用
  • CPO-CNN-GRU-Attention、CNN-GRU-Attention、CPO-CNN-GRU、CNN-GRU四模型多变量时序预测对比
  • 基于Golang的博客系统的设计与实现
  • 【最新】17个一站式数据集成平台案例PPT下载(Apache SeaTunnel )
  • 简易CPU设计入门:本系统中的通用寄存器(三)
  • 实景三维点云处理专业软件ArcGIS根据DSM生成地表点云集
  • IS-2T2R存储器:AWS精度下降问题的解决方案
  • js单例模式
  • Java网络编程之UDP协议介绍及示例代码
  • qingzhou
  • 【Ubuntu】Ubuntu server 18.04 搭建Slurm并行计算环境(包含NFS)
  • WinForm事件遇到异步方法的处理方式
  • 5_SparkGraphX讲解
  • 职场中哪些话中话,弦外之音
  • word中插入zotero引用
  • QT写的动态正弦曲线图显示并打印
  • 多模态机器人
  • 24.小R的随机播放顺序<字节青训营-中等题>
  • 实战指南:Shiro、CAS打造完美单点登录体验
  • 运行python程序报错 undefined symbol: ffi_type_uint32 的参考解决方法