2025年12月GESP(C++五级): 相等序列

题目描述
小 A 有一个包含 N N N 个正整数的序列 A = { A 1 , A 2 , ... , A N } A=\{A_1,A_2,\ldots,A_N\} A={A1,A2,...,AN}。小 A 每次可以花费 1 1 1 个金币执行以下任意一种操作:
- 选择序列中一个正整数 A i A_i Ai( 1 ≤ i ≤ N 1\le i\le N 1≤i≤N),将 A i A_i Ai 变为 A i × P A_i\times P Ai×P, P P P 为任意质数;
- 选择序列中一个正整数 A i A_i Ai( 1 ≤ i ≤ N 1\le i\le N 1≤i≤N),将 A i A_i Ai 变为 A i P \frac{A_i}{P} PAi, P P P 为任意质数,要求 A i A_i Ai 是 P P P 的倍数。
小 A 想请你帮他计算出令序列中所有整数都相同,最少需要花费多少金币。
输入格式
第一行一个正整数 N N N,含义如题面所示。
第二行包含 N N N 个正整数 A 1 , A 2 , ... , A N A_1,A_2,\ldots,A_N A1,A2,...,AN,代表序列 A A A。
输出格式
输出一行,代表最少需要花费的金币数量。
输入输出样例 1
输入 1
5
10 6 35 105 42
输出 1
8
说明/提示
对于 60 % 60\% 60% 的测试点,保证 1 ≤ N , A i ≤ 100 1\le N,A_i\le 100 1≤N,Ai≤100。
对于所有测试点,保证 1 ≤ N , A i ≤ 10 5 1\le N,A_i\le 10^5 1≤N,Ai≤105。
思路分析
这个问题要求通过乘以质数或除以质数(当可整除时)的操作,使得序列中所有数相等,求最小操作次数。
核心思路
-
质因数分解视角:操作的本质是改变每个数的质因数指数:
- 乘以质数 P P P:增加该质数指数的值(+1)
- 除以质数 P P P:减少该质数指数的值(-1)
-
独立处理质因数:每个质因数的操作是独立的。最终所有数相等意味着每个质因数的指数在所有数中都相同。
-
问题转化 :对于每个质数 p p p,我们有一个长度为 N N N 的数组,表示每个数中 p p p 的指数。我们需要通过增减操作(增减1)使所有指数相等,求最小总操作次数。
-
中位数最优性:
- 对于一组数,使它们相等的最优目标值是中位数
- 证明:如果将目标值从 k k k 变为 k + 1 k+1 k+1,那么所有 ≤ k ≤k ≤k 的数需要多操作1次,所有 > k >k >k 的数可以少操作1次,当小于等于目标值的个数超过一半时,继续增加目标值会减少总操作次数,反之亦然。
-
处理未出现的数 :对于某个质数 p p p,有些数可能没有这个质因数(指数为0),这些数也需要考虑在内。
算法步骤
- 对每个数进行质因数分解,记录每个质数的指数
- 对于每个质数 p p p:
- 统计 p p p 在多少个数的质因数分解中出现(记作 c c c)
- 未出现的数有 n − c n-c n−c 个,这些数的指数为0
- 找到所有出现 p p p 的数的指数中位数
- 计算将所有数的 p p p 指数调整到中位数的操作次数
- 累加所有质数的操作次数
代码实现
cpp
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long ll;
ll n;
vector<ll> v[N]; // v[i]存储质数i在所有数中的指数列表
int main(){
cin>>n;
// 1. 质因数分解,记录每个质数的指数
for(int i=1;i<=n;i++){
ll x;
cin>>x;
// 分解质因数
for(int j=2;j<=sqrt(x);j++){
ll cnt=0;
while(x%j==0){
cnt++;
x/=j;
}
if(cnt>0) v[j].push_back(cnt); // 记录质数j的指数
}
// 处理剩余的质因数
if(x!=1) v[x].push_back(1);
}
ll ans=0;
// 2. 对每个质数计算最小操作次数
for(int i=2;i<=100000;i++){
ll c=v[i].size(); // 有多少个数包含质数i
ll zeroc=n-c; // 不包含质数i的数字个数(指数为0)
// 如果没有任何数包含质数i,跳过
if(c==0) continue;
// 对指数进行排序,便于找中位数
sort(v[i].begin(),v[i].end());
// 计算中位数的位置
ll mid=(n-1)/2;
ll zws; // 中位数
// 确定中位数
if(mid<zeroc){
// 中位数落在0的部分
zws=0;
}else{
// 中位数落在非零指数部分
zws=v[i][mid-zeroc];
}
// 3. 计算操作次数
// 对于指数为0的数,每个需要操作zws次(增加zws次)
ll opt=zeroc*zws;
// 对于有指数的数,每个需要操作|指数-中位数|次
for(int j=0;j<v[i].size();j++){
opt+=abs(v[i][j]-zws);
}
// 4. 累加到总答案
ans+=opt;
}
cout<<ans;
return 0;
}
功能分析
时间复杂度
- 质因数分解 :每个数 A i A_i Ai 分解质因数需要 O ( A i ) O(\sqrt{A_i}) O(Ai ),最坏情况下 A i = 10 5 A_i=10^5 Ai=105, A i ≈ 316 \sqrt{A_i}≈316 Ai ≈316,总复杂度 O ( N max A i ) ≈ 3 × 10 7 O(N\sqrt{\max A_i})≈3×10^7 O(NmaxAi )≈3×107
- 排序部分 :每个质数对应的指数列表排序,总指数个数为所有数的质因数总个数,约为 O ( N log max A i ) O(N\log \max A_i) O(NlogmaxAi)
- 总体复杂度可以接受
空间复杂度
- 使用
vector<ll> v[N]存储每个质数的指数列表 - 每个数最多有 O ( log A i ) O(\log A_i) O(logAi) 个质因数,总空间 O ( N log max A i ) O(N\log \max A_i) O(NlogmaxAi)
正确性验证
以样例为例:
输入:5
10 6 35 105 42
质因数分解:
- 2: 在10(1), 6(1), 42(1)中出现,指数列表[1,1,1],有2个0
- 中位数是1,操作次数 = 2×1 + (|1-1|×3) = 2
- 3: 在6(1), 105(1), 42(1)中出现,指数列表[1,1,1],有2个0 → 2次操作
- 5: 在10(1), 35(1), 105(1)中出现,指数列表[1,1,1],有2个0 → 2次操作
- 7: 在35(1), 105(1), 42(1)中出现,指数列表[1,1,1],有2个0 → 2次操作
总操作次数 = 2+2+2+2 = 8,与样例输出一致。
关键点
- 中位数选择:选择中位数作为目标值能最小化绝对差之和
- 零指数处理:未出现的数(指数为0)必须考虑在内
- 质数范围:只需要处理实际出现的质数,不需要遍历所有1e5以内的质数
- 独立处理:不同质因数的操作相互独立,可以分别计算后求和
各种学习资料,助力大家一站式学习和提升!!!
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"########## 一站式掌握信奥赛知识! ##########";
cout<<"############# 冲刺信奥赛拿奖! #############";
cout<<"###### 课程购买后永久学习,不受限制! ######";
return 0;
}
- 一、CSP信奥赛C++通关学习视频课:
- C++语法基础
- C++语法进阶
- C++算法
- C++数据结构
- CSP信奥赛数学
- CSP信奥赛STL
- 二、CSP信奥赛C++竞赛拿奖视频课:
- 信奥赛csp-j初赛高频考点解析
- CSP信奥赛C++复赛集训课(12大高频考点专题集训)
- 三、考级、竞赛刷题题单及题解:
- GESP C++考级真题题解
- CSP信奥赛C++初赛及复赛高频考点真题解析
- CSP信奥赛C++一等奖通关刷题题单及题解
详细内容:
1、csp/信奥赛C++,完整信奥赛系列课程(永久学习):
https://edu.csdn.net/lecturer/7901 点击跳转



2、CSP信奥赛C++竞赛拿奖视频课:
https://edu.csdn.net/course/detail/40437 点击跳转

3、csp信奥赛冲刺一等奖有效刷题题解:
CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转
- 2025 csp-j 复赛真题及答案解析(最新更新)
- 2025 csp-x(山东) 复赛真题及答案解析(最新更新)
- 2025 csp-x(河南) 复赛真题及答案解析(最新更新)
- 2025 csp-x(辽宁) 复赛真题及答案解析(最新更新)
- 2025 csp-x(江西) 复赛真题及答案解析(最新更新)
- 2025 csp-x(广西) 复赛真题及答案解析(最新更新)
- 2020 ~ 2024 csp 复赛真题题单及题解
- 2019 ~ 2022 csp-j 初赛高频考点真题分类解析
- 2021 ~ 2024 csp-s 初赛高频考点解析
- 2023 ~ 2024 csp-x (山东)初赛真题及答案解析
- 2024 csp-j 初赛真题及答案解析
- 2025 csp-j 初赛真题及答案解析(最新更新)
- 2025 csp-s 初赛真题及答案解析(最新更新)
- 2025 csp-x (山东)初赛真题及答案解析(最新更新)
- 2025 csp-x (江西)初赛真题及答案解析(最新更新)
- 2025 csp-x (辽宁)初赛真题及答案解析(最新更新)
CSP信奥赛C++一等奖通关刷题题单及题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转
- 129 道刷题练习和详细题解,涉及:模拟算法、数学思维、二分算法、 前缀和、差分、深搜、广搜、DP专题、 树和图
4、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转
· 文末祝福 ·
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"跟着王老师一起学习信奥赛C++";
cout<<" 成就更好的自己! ";
cout<<" csp信奥赛一等奖属于你! ";
return 0;
}