算法基础(数论)—算法基本定理

算术基本定理

【算术基本定理】
算术基本定理⼜称 唯⼀分解定理
• 任何⼀个⼤于1 的⾃然数 ,都可以唯⼀分解成有限个质数的乘积

【代码实现】

cpp 复制代码
int c[N]; // c[i] 表⽰ i 这个质数出现的次数
void deprime(int x)
{
for(int i = 2; i <= x / i; i++)
{
int cnt = 0;
while(x % i == 0) // 只要有这个因⼦,就除尽,并且计数
{
x /= i;
cnt++;
}
c[i] += cnt;
}
if(x > 1) c[x]++; // 不要忘记判断最后⼀个质数
}

时间复杂度:
枚举到 n ,因此时间复杂度为 O ( N ) 。但是最优情况下会达到 O (log n ) 。


1 质因⼦分解

题⽬来源: 洛⾕
题⽬链接: P2043 质因⼦分解
难度系数: ★
题目描述

对 N! 进行质因子分解。

输入格式

输入数据仅有一行包含一个正整数 N,N≤10000。

输出格式

输出数据包含若干行,每行两个正整数 p,a,中间用一个空格隔开。表示 N! 包含 a 个质因子 p,要求按 p 的值从小到大输出。

输入输出样例

输入 #1复制

复制代码
10

输出 #1复制

复制代码
2 8
3 4
5 2
7 1

说明/提示

10!=3628800=(28)×(34)×(52)×7。

【解法】

对阶乘中的每⼀个数,分解质因数。


【参考代码】

cpp 复制代码
#include <iostream>
using namespace std;

// 定义数组最大范围:题目要求 N ≤ 10000,所以开 10000+10 足够
const int N = 1e4 + 10;

// n:输入的阶乘上限;c数组:c[p] 记录质因数p在N!中出现的总次数
int n;
int c[N];

// 试除法分解单个数字x的质因数,并把次数累加到c数组
void deprime(int x)
{
    // 枚举 2 到 √x 的所有数(试除法核心:因数成对出现,只需枚举到平方根)
    for (int i = 2; i <= x / i; i++)
    {
        int cnt = 0; // 记录当前质因数i在x中出现的次数
        // 只要x能被i整除,就反复除,统计次数
        while (x % i == 0)
        {
            cnt++;   // 次数+1
            x /= i;  // x除以i,缩小继续判断
        }
        c[i] += cnt; // 把当前质因数的次数累加到全局计数数组
    }
    // 特殊情况:如果最后x>1,说明剩下的x是一个质数(大于√原x的质因数)
    if (x > 1)
        c[x]++;
}

int main()
{
    // 输入阶乘上限N
    cin >> n;

    // 遍历 2~n 的所有数,逐个分解质因数
    for (int i = 2; i <= n; i++)
    {
        deprime(i);
    }

    // 遍历 2~n,输出有次数的质因数(按p从小到大)
    for (int i = 2; i <= n; i++)
    {
        // 只有次数>0的质因数才输出
        if (c[i])
        {
            cout << i << " " << c[i] << endl;
        }
    }

    return 0;
}

2 阶乘分解

题⽬来源: 洛⾕
题⽬链接: P10495 阶乘分解
难度系数: ★★
题目描述

给定整数 N(3≤N≤106),试把阶乘 N! 分解质因数,按照算术基本定理的形式输出分解结果中的 pi​ 和 ci​ 即可。

输入格式

一个整数 N。

输出格式

N! 分解质因数后的结果,共若干行,每行一对 pi​,ci​,表示含有 pici​​ 项。按照 pi​ 从小到大的顺序输出。

输入输出样例

输入 #1复制

复制代码
5

输出 #1复制

复制代码
2 3
3 1
5 1

【解法】

正难则反。
先筛出 [1, 1 e 6] 之间的质数,然后考虑包含某⼀个质数 j 次幂的数⼀共有多少个。


【参考代码】

cpp 复制代码
#include <iostream>
using namespace std;

// 定义长整型别名,防止乘法溢出
typedef long long LL;
// 数组上限:题目要求 N ≤ 1e6,额外预留10个位置避免越界
const int N = 1e6 + 10;

// 全局变量:
int n;                  // 输入的阶乘上限 N
bool st[N];             // 标记数组:st[i] = true 表示 i 不是质数
int p[N], cnt;          // p数组存储质数,cnt 记录质数的个数

// 线性筛(欧拉筛):快速筛出 1~n 范围内的所有质数
void get_prime()
{
    // 遍历 2 到 n 的所有数
    for (int i = 2; i <= n; i++)
    {
        // 如果 i 未被标记,说明是质数,存入 p 数组
        if (!st[i])
        {
            p[++cnt] = i;
        }
        // 用已找到的质数筛除合数(线性筛核心)
        for (int j = 1; 1LL * i * p[j] <= n; j++)
        {
            // 标记 i*p[j] 为合数
            st[i * p[j]] = true;
            // 关键:避免重复筛除,保证每个合数只被最小质因数筛一次
            if (i % p[j] == 0)
            {
                break;
            }
        }
    }
}

int main()
{
    // 输入阶乘上限 N
    cin >> n;

    // 第一步:筛出 1~n 范围内的所有质数
    get_prime();

    // 第二步:遍历每个质数,计算其在 N! 中的次数
    for (int i = 1; i <= cnt; i++)
    {
        int prime = p[i];  // 当前要计算的质数
        int count = 0;     // 记录该质数在 N! 中的总次数

        // 公式:次数 = n//p + n//p² + n//p³ + ... 直到 p^k > n
        for (LL j = prime; j <= n; j *= prime)
        {
            count += n / j;
        }

        // 输出质数和对应的次数
        cout << prime << " " << count << endl;
    }

    return 0;
}
相关推荐
啊阿狸不会拉杆6 小时前
《数字图像处理》第 12 章 - 目标识别
图像处理·人工智能·算法·计算机视觉·数字图像处理
进击的横打6 小时前
【车载开发系列】安全算法与安全访问
算法·安全·车载系统
努力学算法的蒟蒻6 小时前
day59(1.18)——leetcode面试经典150
算法·leetcode·职场和发展
666HZ6666 小时前
数据结构3.0 栈、队列和数组
开发语言·数据结构·算法
知乎的哥廷根数学学派6 小时前
基于物理引导和不确定性量化的轻量化神经网络机械退化预测算法(Python)
人工智能·pytorch·python·深度学习·神经网络·算法·机器学习
程序员-King.6 小时前
day146—递归—验证二叉搜索树(LeetCode-98)
算法·leetcode·二叉树·递归
永远都不秃头的程序员(互关)6 小时前
【K-Means深度探索(五)】不止欧氏距离:K-Means中距离度量那些事儿
算法·机器学习·kmeans
tobias.b6 小时前
408真题解析-2009-41-数据结构-最短路径
数据结构·算法·计算机考研·408考研·408真题解析
王老师青少年编程6 小时前
2024年9月GESP真题及题解(C++七级): 矩阵移动
c++·算法·题解·真题·gesp·七级·矩阵移动
txinyu的博客6 小时前
连接池问题
服务器·网络·c++