题解:洛谷 P14918 相等序列

【题目来源】

洛谷:P14918 [GESP202512 五级] 相等序列 - 洛谷

【题目描述】

小 A 有一个包含 \(N\) 个正整数的序列 \(A=\{A_1,A_2,\ldots,A_N\}\)。小 A 每次可以花费 \(1\) 个金币执行以下任意一种操作:

  • 选择序列中一个正整数 \(A_i\)\(1\le i\le N\)),将 \(A_i\) 变为 \(A_i\times P\)\(P\) 为任意质数;
  • 选择序列中一个正整数 \(A_i\)\(1\le i\le N\)),将 \(A_i\) 变为 \(\frac{A_i}{P}\)\(P\) 为任意质数,要求 \(A_i\)\(P\) 的倍数。

小 A 想请你帮他计算出令序列中所有整数都相同,最少需要花费多少金币。

【输入】

第一行一个正整数 \(N\),含义如题面所示。

第二行包含 \(N\) 个正整数 \(A_1,A_2,\ldots,A_N\),代表序列 \(A\)

【输出】

输出一行,代表最少需要花费的金币数量。

【输入样例】

5
10 6 35 105 42

【输出样例】

8

【核心思想】

  1. 问题分析:给定 \(N\) 个正整数,每次花费1金币可以将某数乘或除以任意质数 \(P\)(除法要求整除)。求使所有数相等的最少金币。这是一个质因数分解 + 中位数贪心问题,关键在于:两个数相等当且仅当它们的所有质因数的指数都相同,因此问题转化为对每个质数独立求使所有指数相同的最小操作次数。

  2. 算法选择

    • 质因数分解:用埃氏筛预处理质数,对每个数分解质因数,统计每个质数 \(p_i\) 在各个数中的指数
    • 中位数贪心:对每个质数 \(p_i\),设其在 \(N\) 个数中的指数分别为 \(e_1, e_2, \ldots, e_N\)。使所有指数相等的最小操作次数 = \(\sum |e_j - target|\),其中 \(target\) 取中位数时最小
    • 频率统计优化:由于指数范围小,用 a[i][j] 统计第 \(i\) 个质数的指数 \(j\) 出现的次数,然后贪心选择出现次数更多的那边
  3. 关键步骤

    • 埃氏筛预处理:生成 \(10^5\) 以内的质数,记录每个质数的索引 isprime[i]
    • 质因数分解(对每个数 \(x\)):
      • 遍历质数 \(p_j\)\(\sqrt{x}\)
        • \(p_j | x\),计算指数 \(cnt\)a[j][cnt]++
      • 若剩余 \(x > 1\)a[isprime[x]][1]++
    • 计算答案(对每个质数 \(i\),每个指数 \(j\)):
      • a[i][j] > N/2ans += N - a[i][j](将少数变为多数)
      • 否则:ans += a[i][j](将少数变为0,即消去该质因数)
    • 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(MAX \log \log MAX + N \cdot \sqrt{A_{\max}}/\log A_{\max})\),埃氏筛 + 质因数分解
    • 空间复杂度:\(O(MAX + N \cdot \log A_{\max})\),质数表和指数统计数组
  5. 质因数分解 + 中位数贪心的核心思想

    • 问题分解:乘除质数操作只改变对应质因数的指数,各质因数独立,可分别计算后求和
    • 指数对齐:两个数相等 \(\Leftrightarrow\) 所有质因数指数相同。对每个质数,需要将所有数的该质因数指数统一
    • 贪心策略:对于指数 \(j\),若出现次数 \(> N/2\),则将其他数变为指数 \(j\) 更优;否则将指数为 \(j\) 的数变为 \(0\)(消去)更优。这等价于选择中位数或 \(0\) 作为目标
    • 频率统计:不存储每个数的具体指数,而是统计每个指数出现的频率,利用多数原则贪心决策
    • 适用于"质因数指数统一"问题,核心在于独立分解和频率统计贪心

【解题思路】

【算法标签】

普及 #质数

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, a[N][20], p[10005], cur, isprime[100005], ans;  // n: 数字个数, a: 存储质因数统计, p: 质数数组, cur: 质数个数, isprime: 判断质数并存储索引, ans: 答案

int main()
{
    cin >> n;  // 输入数字个数

    // 埃拉托色尼筛法预处理质数
    for (int i = 2; i <= 100000; i++)
    {
        if (!isprime[i])  // 如果i是质数
        {
            p[++cur] = i;  // 将质数i存入数组p
            isprime[i] = cur;  // 记录质数i在数组p中的索引
            for (int j = i + i; j <= 100000; j += i)  // 标记i的所有倍数
                isprime[j] = 1;  // 标记为非质数
        }
    }

    // 处理输入的n个数字
    for (int i = 1; i <= n; i++)
    {
        int x;
        cin >> x;  // 输入一个数字

        // 分解质因数
        for (int j = 1; p[j] * p[j] <= x; j++)  // 只需检查到sqrt(x)
        {
            if (x % p[j] == 0)  // 如果p[j]是x的质因数
            {
                int cnt = 0;  // 记录当前质因数的指数
                while (x % p[j] == 0)  // 计算质因数p[j]的指数
                {
                    cnt++;
                    a[j][cnt]++;  // 统计第j个质数的cnt次方在n个数字中出现的次数
                    x /= p[j];  // 除掉这个质因数
                }
            }
        }
        if (x)  // 如果x还有剩余的质因数(x本身是质数且大于sqrt(原x))
            a[isprime[x]][1]++;  // 统计这个质数的一次方
    }

    // 计算结果
    for (int i = 1; i <= cur; i++)  // 遍历所有质数
        for (int j = 1; a[i][j]; j++)  // 遍历第i个质数的所有指数
        {
            if (a[i][j] > n / 2)  // 如果该质因子指数出现的次数超过一半
                ans += n - a[i][j];  // 添加需要改变的个数
            else
                ans += a[i][j];  // 添加该指数出现的次数
        }

    cout << ans << endl;  // 输出结果
    return 0;
}

【运行结果】

5
10 6 35 105 42
8
posted @ 2026-08-10 12:26  团爸讲算法  阅读(42)  评论(0)    收藏  举报