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

算术基本定理

【算术基本定理】
算术基本定理⼜称 唯⼀分解定理
• 任何⼀个⼤于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;
}
相关推荐
nlpming38 分钟前
OpenCode Skills 文档
算法
无限进步_41 分钟前
二叉搜索树完全解析:从概念到实现与应用场景
c语言·开发语言·数据结构·c++·算法·github·visual studio
鱼跃厂长1 小时前
这份skill,能将你的简历提升到字节的水平!
c++·ai·ai编程
05候补工程师1 小时前
深度解构 ROS 2:如何手动调通 Nav2 A* 路径规划引擎
linux·人工智能·经验分享·算法·机器人
上弦月-编程1 小时前
【C语言逻辑题】谋杀案凶手是谁?——经典矛盾推理题详解
算法
天若有情6731 小时前
逆向玩家狂喜!用C++野生写法一键破解线性加密(不规范但巨好用)
开发语言·c++·算法
咸鱼翻身小阿橙1 小时前
Qt QML调用C++注册类
java·c++·qt
风筝在晴天搁浅2 小时前
剑指Offer 60.n个骰子的点数
算法
ProgramHelpOa2 小时前
Optiver 2026 OA 全面复盘|26NG / Intern 最新高频题型整理
人工智能·算法·机器学习