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

算术基本定理

【算术基本定理】
算术基本定理⼜称 唯⼀分解定理
• 任何⼀个⼤于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;
}
相关推荐
灵感__idea6 小时前
Hello 算法:众里寻她千“百度”
前端·javascript·算法
Wect16 小时前
LeetCode 130. 被围绕的区域:两种解法详解(BFS/DFS)
前端·算法·typescript
NAGNIP1 天前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
端平入洛1 天前
delete又未完全delete
c++
颜酱2 天前
单调栈:从模板到实战
javascript·后端·算法
CoovallyAIHub2 天前
仿生学突破:SILD模型如何让无人机在电力线迷宫中发现“隐形威胁”
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
从春晚机器人到零样本革命:YOLO26-Pose姿态估计实战指南
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
Le-DETR:省80%预训练数据,这个实时检测Transformer刷新SOTA|Georgia Tech & 北交大
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
强化学习凭什么比监督学习更聪明?RL的“聪明”并非来自算法,而是因为它学会了“挑食”
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
YOLO-IOD深度解析:打破实时增量目标检测的三重知识冲突
深度学习·算法·计算机视觉