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

算术基本定理

【算术基本定理】
算术基本定理⼜称 唯⼀分解定理
• 任何⼀个⼤于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;
}
相关推荐
CoderCodingNo2 小时前
【GESP】C++五级真题(结构体排序考点) luogu-B3968 [GESP202403 五级] 成绩排序
开发语言·c++·算法
YGGP3 小时前
【Golang】LeetCode 32. 最长有效括号
算法·leetcode
自然常数e4 小时前
字符函数和字符串函数
c语言·算法·visual studio
leaves falling4 小时前
c语言分数求和
算法
Das14 小时前
【机器学习】01_模型选择与评估
人工智能·算法·机器学习
星轨初途5 小时前
郑州轻工业大学2025天梯赛解题
c++·经验分享·笔记·算法·链表·剪枝
不忘不弃5 小时前
从字符串中提取数字
数据结构·算法
点云SLAM5 小时前
C++ 引用折叠(Reference Collapsing)和示例讲解说明
数据结构·c++·标准算法·完美转发·代码性能优化·c++ 引用折叠·typedef / using