河南萌新联赛2025第(四)场:L(数论+素数筛)
链接:https://ac.nowcoder.com/acm/contest/115184/L
题目要求
故障机器人探索完尖塔后得到了 n 个遗物,其中第 i 个遗物的价值是 \(a_i\)。如果一个遗物的价值的因数个数是质数且不等于 2,那么故障机器人就认为这个遗物是一个完美遗物。故障机器人想要知道其拥有的完美遗物的价值之和是多少。
输入要求
第一行,输入一个整数 \(n (1≤n≤2⋅10^5)\),表示故障机器人所拥有遗物的数量。
第二行,输入 n 个整数 \(a_1,a_2,…,a_n(1≤a_i≤10^{12})\),其中 \(a_i\) 表示故障机器人第 i 个遗物的价值。
输出要求
一行,一个整数,表示故障机器人所拥有的完美遗物的价值之和。
题目分析
①判断当前遗物是否为完美遗物(三个步骤如下):
1、计算该遗物价值的因数个数t;
2、该个数t为质数;
3、t≠2。
②将所有完美遗物的价值求和。
拆分步骤
①计算一个数的因数的个数,
已知数论里的一个基本结论:
一个正整数 n 分解成素因数乘积后:
\[n=p_1^{a_1} \cdot p_2^{a_2} \cdot \cdots \cdot p_k^{a_k}
\]
那么它的因子个数是:
\[d(n) = (a_1 + 1)(a_2 + 1) \cdots (a_k + 1)
\]
②由于我们需要判断其个数是否为质数,所以在此之前,我们需要写一个素数筛,对数组进行预处理方便后续的判断直接使用,这里我用的是经典的埃拉托色尼筛法。
代码实现思路
- ①预处理素数数组(经典的埃拉托色尼筛法)
埃拉托色尼筛法模板
vector<bool> prim(int n)
vector<bool> a(n+1,true);
a[0]=a[1]=false;
for(int i=2;i*i<=n;i++){
if(a[i]){
for(int j=i*i;j<=n;j+=i){
a[j]=false;
}
}
}
return a;
}
vector<bool> a=prim(1e7);
- ②将题目数据范围内的数预处理(直接算出该数是否是完美遗产)
因为$ a_i+1≥2$,每一项 ≥2,并且至少有两项(k≥2)时,这个乘积一定不是质数。
只有当 n 是形如 \(p^q\) 的数(即只有一个素因子)时,才可能让 \(d(n)=q+1\) 是质数。
所以为了使 d(n) 是质数,n 必须是某个质数 p 的 q 次幂\((n=p^q)\),而且 q+1 也必须是质数(因为 d(n)=q+1)。
因此我们可以直接通过枚举,将所有符合条件的完美遗产数标记为真。
完整题解如下
#include <iostream>
#include <vector>
#include <cmath>
#define int long long
using namespace std;
const int N = 1e6;
vector<bool> is_prim;
vector<int> get_prim(int n){
is_prim.assign(n+1, true);
is_prim[0]=is_prim[1]=false;
vector<int> prim;
for(int i=2;i<n;i++){
if(is_prim[i]){
prim.push_back(i);
for(int j=i*2;j<=n;j+=i){
is_prim[j]= false;
}
}
}
return prim;
}
signed main() {
int n;
cin >> n;
vector<int> prim= get_prim(N);
vector<bool> judge(N+1, false);
for(auto p:prim){
int i=2;
int x=p*p;
while(x<N*N){
if(i+1<is_prim.size()&&is_prim[i+1])
{
int q= sqrtl(x);
if(q*q==x) judge[q]=true;
}
i++;
x*=p;
}
}
int numb,ans=0;
for(int i=0;i<n;i++){
cin>>numb;
int m= sqrtl(numb);
if(m*m==numb&&judge[m]){
ans+=numb;
}
}
cout<<ans<<endl;
return 0;
}

浙公网安备 33010602011771号