约数个数(数论,蓝桥杯)

题目描述:

给定一个数n,再给出n个数,现在要求你求出这些数的乘积的约数个数总和,结果对1e9+7取模。

取值范围:1<n<100; 1<ni<2e9;

分析步骤:

第一:要求约数的个数,我们有许多的求法,比如试除法,线性筛法求。那么本文就将两种方法都讲一遍。题目中说要求出给出的数的乘积的约数个数,如果我们真的去求出了数的乘积总和,那么一定会溢出int函数因为 ni 最大值为2e9只要稍微再乘个数都会超过,所以我们仔细想想,其实我们只需要把每一个数都拆成约数,把他们的约数是哪些分别都统计一下,再结合公式就可以得到最终的答案

第二:试除法求出约数个数。

输入一个n,再用while循环不断地将数输入进去,定义有一个哈希表first代表着具体的约数是哪个;second代表着这个约数有多少。

用for循环不断地去寻找,如果 i 是x的约数的话那么就将mpi++那么约束大小为i的个数就++,再将 x 除去i ,直到 i 不再是x的约数的话,while循环结束。在判断一下最终剩下来的数是不是 1 , 如果不是1的话那么这个数就也是一个约数我们将其存储起来。

定义res为1,运用迭代器向后遍历,利用公式将每一个约数的个数+1的和相乘得到的就是最终的答案。

cpp 复制代码
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const int N = 1e6+10 , MOD = 1e6+1;

unordered_map<int, int >mp;

int main()
{
    int n ; 
    cin>>n;
    while(n --){
        int x ; 
        cin>>x;
        for(int i = 2 ; i <= x / i ; i++){
            while(x % i == 0){
                mp[i]++;
                x/=i;
            }
        }
     if(x > 1) mp[x]++;
    }
    LL res = 1;
    for(auto t : mp){
        res *= (1+t.second) % MOD;
    }
    cout<<res;
    return 0;
}

但是我们知道其实试除法求出约数个数时间复杂度为根号n,那么当求1~n的所有约数的个数的时候时间复杂度为n根号n那么只要数据量稍微大一点就会超时,所以接下来我们介绍欧拉筛也就是线性筛法将时间复杂度优化为n。

第三:线性筛法求出1~n的约数个数

既然是线性筛法我们就要套用线性筛的模板,

首先明确一下我们的数组分别是有什么用 pN 收集质数, visN判断此数是否遍历过 , aN记录 i 的最小质数出现的次数 ; dN i 数的约数个数;

初始化d1为1,用for循环去遍历,如果这个值是没有被遍历过的那么这个值就一定是质数,我们把他收集进入我们的 p 数组,更新di为2,因为由于该数是个质数那么他的约数就一定只有1 和 他们本身,所以di的约数个数为2,更新ai为1,因为该数是质数所以他最小的质数出现的次数也一定是他本身所以是1。

再用for循环让后面的值一定只被他最小的质因子给除去我们将该点定义为true表示该点不是质数,

进入判断如果:i % pj == 0 。 由于pj一定是它最小的质因子所以这个式子成立的话,就代表着am 比 ai 多了一个质因子所以加1.所以现在的dm和原来的比起来,就只有c1那部分有点差别,所以只要除去原来的部分,再乘上新加的部分就可以得出答案了。

如果判断式子不成立的话则代表这个数是一个新的质因子,因为新的质因子的话那就只有两个约数(1和本身),只有一个质因子(本身),所以我们更新am为1,更新dm则为2*di因为我们看看公式他是要将质因子个数+1的和相乘,所以比原来多了一个质因子套入公式(1+1)== 2 所以是原来的两倍。

cpp 复制代码
void get_d(int n){
     d[1] = 1;
     for(int i = 2 ; i <= n ; i ++){
         if(!vis[i]){
             p[cnt++] = i;
             d[i] = 2;
             a[i] = 1;
         }
         for(int j = 0; p[j] * i <= n ; j ++){
             int m = p[j] * i;
             vis[m] = true;
             if(i % p[j] == 0){
                 a[m] = a[i]+1;
                 d[m] = d[i]/a[m]*(a[m]+1);
                 break;
             }else{
                 d[m] = 2*d[i];
                 a[m] = 1;
             }   
         }
     }
}

线性筛代码:

cpp 复制代码
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1e6+10;

int p[N] , vis[N] , cnt;
int a[N] ;
int d[N] ;

void get_d(int n){
     d[1] = 1;
     for(int i = 2 ; i <= n ; i ++){
         if(!vis[i]){
             p[cnt++] = i;
             d[i] = 2;
             a[i] = 1;
         }
         for(int j = 0; p[j] * i <= n ; j ++){
             int m = p[j] * i;
             vis[m] = true;
             if(i % p[j] == 0){
                 a[m] = a[i]+1;
                 d[m] = d[i]/a[m]*(a[m]+1);
                 break;
             }else{
                 d[m] = 2*d[i];
                 a[m] = 1;
             }   
         }
     }
}

int main()
{
    
    int x ; 
    cin>>x;
    get_d(x);
    cout<<d[x]<<endl;
    return 0;
}
相关推荐
2601_9545267510 小时前
【工业传感与算法实战】温漂补偿与零点抗漂破局:基于二阶多项式拟合的 C/C++ 边缘校准算法,深度拆解“压力变送器什么牌子好”的技术硬指标
c语言·c++·算法
code_pgf10 小时前
`unordered_map` 详解
c++
叩码以求索11 小时前
浅谈:算法萌新如何高效刷题应对面试(一)
算法·面试·职场和发展
一拳一个呆瓜12 小时前
【STL】iostream 编程:检测提取操作产生的错误
c++·stl
稚南城才子,乌衣巷风流12 小时前
判断素数与拓展应用
c++
众少成多积小致巨12 小时前
C++ 规范参考(下)
c++
c2385612 小时前
把 C++ 内存分配拆透:new 与 malloc 的三层血缘
开发语言·c++·算法
aaaameliaaa13 小时前
指针之总结
c语言·笔记·算法
宵时待雨13 小时前
优选算法专题9:哈希表
数据结构·算法·散列表
txzrxz13 小时前
最短路问题——Dijkstra 算法
数据结构·c++·算法·最短路·优先队列