河南萌新联赛2025第(四)场【补题】 - 指南

A-完美序列

题目链接
在这里插入图片描述

解题思路

所谓完美序列,就是让我们找给定的序列a中,选任意元素任意排列顺序,能组成最长的一个满足在这里插入图片描述
的序列的长度。那么我们可以用哈希表mp记录每个数字的个数,然后遍历可能的和的值2~10000,第二层循环从1 ~i/2,为了缩短时间同时防止重复记录,然后再令y=i-j,看满足这个条件的数有多少个,如果他的值和j相等,组合对数就加mp[j]/2,否则就取二者个数的最小值,最后更新一下最大值ans。输出即可。

AC代码

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define int long long
  #define PII pair<
  int,int>
  #define fi first
  #define se second
  #define endl '\n'
  const int N=2e5+5;
  const int inf=5000;
  int n,num,mp[inf+10];
  void solve()
  {
  cin>>n;
  for(int i=1;i<=n;i++)
  cin>>num,mp[num]++;
  int ans=0;
  for(int i=2;i<=10000;i++)
  {
  int res=0;
  for(int j=1;j<=i/2;j++)
  {
  int x=i-j;
  if(x>inf)continue;
  if(x==j)
  res+=mp[x]/2;
  else
  res+=min(mp[x],mp[j]);
  }
  ans=max(res,ans);
  }
  cout<<ans*2;
  }
  signed main()
  {
  IOS;
  int _=1;
  // cin>>_;
  while(_--)
  solve();
  return 0;
  }

C-合并石子

在这里插入图片描述

解题思路

这道题我是分别记录位置i的左边所有数到达它需要的步数和右边所有数需要到达他的总步数,最后再将二者相加就是所有石子需要到达他的总步数。通过前缀和记录每个位置需要移动的石子个数,(数字+k-1)/k是向上取整的意思,再将每个位置的步数累加。然后遍历筛选一个最小的总和,ans初始值不能赋INT_MAX,不然会wa一半。。。因为题目中数字较大,所以初值赋1e18.

AC代码

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define int long long
  #define PII pair<
  int,int>
  #define fi first
  #define se second
  #define endl '\n'
  const int N=1e6+6;
  int n,k,a[N],b[N],c[N],s1[N],s2[N],l[N],r[N];
  void solve()
  {
  cin>>n>>k;
  for(int i=1;i<=n;i++)
  cin>>a[i];
  for(int i=1;i<=n;i++)
  l[i]=l[i-1]+a[i];
  for(int i=n;i>=1;i--)
  r[i]=r[i+1]+a[i];
  for(int i=1;i<=n;i++)
  s1[i]=s1[i-1]+(l[i]+k-1)/k;
  for(int j=n;j>=1;j--)
  s2[j]=s2[j+1]+(r[j]+k-1)/k;
  int ans=1e18;
  for(int i=1;i<=n;i++)
  ans=min(ans,s1[i-1]+s2[i+1]);
  cout<<ans;
  }
  signed main()
  {
  IOS;
  int _=1;
  // cin>>_;
  while(_--)
  solve();
  return 0;
  }

G-封闭运算

题目链接
在这里插入图片描述

解题思路

将原有的值都标记,数据范围很小,开双层循环遍历,如果二者异或值在原数组中不存在就输出”NO"如果全都存在就输出“YES".

AC代码

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define int long long
  #define PII pair<
  int,int>
  #define fi first
  #define se second
  #define endl '\n'
  const int N=106;
  int n,a[N];
  map<
  int,int>mp;
  void solve()
  {
  cin>>n;
  int f=1;
  for(int i=1;i<=n;i++)
  cin>>a[i],mp[a[i]]++;
  for(int i=1;i<=n;i++)
  {
  for(int j=1;j<=n;j++)
  {
  int x=a[i]|a[j];
  if(mp[x]==0)
  {
  f=0;
  break;
  }
  }
  if(!f)
  break;
  }
  if(f)
  cout<<
  "YES";
  else
  cout<<
  "NO";
  }
  signed main()
  {
  IOS;
  int _=1;
  // cin>>_;
  while(_--)
  solve();
  return 0;
  }

J-故障机器人的完美牌组

题目链接
在这里插入图片描述

解题思路

简单的规律题,不过我错了很多发,第一看错题了,第二考虑少了。
首先如果是1,特判直接输出。
否则就从2~n找到最大值mx和最大值所在的位置j,然后看这个最大值是不是等于0,如果是,就不做处理,因为处理了不仅没有数变大而且长度会变小字典序就变小了。如果不是0,就将j标记,a[1]=a[1]+a[j],输出n-1,然后输出剩下的数,便利到j时不输出,因为已经用掉了。

AC代码

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define int long long
  #define PII pair<
  int,int>
  #define fi first
  #define se second
  #define endl '\n'
  const int N=1e6;
  int n,a[N],mx=-1e10,j;
  unordered_map<
  int,int>b;
  void solve()
  {
  cin>>n;
  for(int i=1;i<=n;i++)
  cin>>a[i],b[a[i]]++;
  if(n==1)
  {
  cout<<n<<endl;
  cout<<a[1]<<endl;
  return ;
  }
  for(int i=2;i<=n;i++)
  {
  if(a[i]>=mx)
  {
  mx=a[i];
  j=i;
  }
  }
  if(mx!=0)
  {
  cout<<n-1<<endl;
  cout<<a[1]+mx<<
  " ";
  for(int i=2;i<=n;i++)
  {
  if(i!=j)
  cout<<a[i]<<
  " ";
  }
  }
  else
  {
  cout<<n<<endl;
  for(int i=1;i<=n;i++)
  cout<<a[i]<<
  " ";
  }
  }
  signed main()
  {
  IOS;
  int _=1;
  // cin>>_;
  while(_--)
  solve();
  return 0;
  }

L-故障机器人的完美遗物

题目链接
在这里插入图片描述

解题思路

这道题的核心是判断一个数是否为“完美遗物”,并计算所有完美遗物的价值之和。完美遗物的定义是:该数的因数个数是质数且不等于 2。首先筛出可以开平方的数,这样它的因数一定是奇数个,就有可能是质数。

1. 数学原理(题解核心)

根据**数论唯一分解定理**,任何正整数 ( n ) 可分解为素数幂的乘积:[ n = \prod_{i=1}^k p_i^{\alpha_i} \quad (p_i \text{ 是素数,} \alpha_i \text{ 是正整数}) ]其**因数个数** ( d(n) ) 的计算公式为:[ d(n) = \prod_{i=1}^k (\alpha_i + 1) ]

要让 ( d(n) ) 是质数且 ≠ 2,需满足:

  • 因数个数的乘积结果是质数 → 乘积中只能有一个因子(否则质数相乘会得到合数)。
  • 因此,( n ) 的素因数分解形式必须是 ( n = a^{b-1} )(其中 ( a ) 是素数,( b ) 是质数且 ( b \neq 2 ) )。此时:
    [ d(n) = (b-1 + 1) = b ]
    ( b ) 是质数且 ≠ 2,满足“完美遗物”条件。

2. 实现思路

(1)预处理素数(prime 函数)
埃氏筛预处理出一定范围(如 ( 10^6 ) )内的素数,后续用于快速分解因数。

(2)因数个数计算(con 函数)
对输入的数 ( x ),先判断是否为平方数(因为完美遗物的形式是 ( a^{b-1} ),等价于平方数的变形)。若为平方数,对其平方根 ( \text{sqrt}(x) ) 做素因数分解,计算因数个数:

  • 分解平方根的素因数,得到各素因子的指数 ( d )。
  • 因数个数公式变为 ( \prod (2d + 1) )
    (因为原数是平方根的平方,指数翻倍)。

(3)筛选完美遗物(solve 函数)
遍历所有遗物值:

  • 先判断是否为平方数(否则不可能满足 ( n = a^{b-1} ) 形式)。
  • 对平方根分解,计算因数个数 ( f )。
  • 检查 ( f ) 是否是质数且 ≠ 2,若是则累加价值。

(1)埃氏筛预处理素数

const int N = 1e6 + 5000;
int a[N], b[N], q, k = 0;
void prime() {
a[1] = 1;
// 1 不是素数
for (int i = 2; i < N; i++) {
if (a[i] == 0) b[++k] = i;
// 记录素数
for (int j = 1; j <= k && i * b[j] < N; j++) {
a[i * b[j]] = 1;
// 标记非素数
if (i % b[j] == 0) break;
// 保证每个合数只被最小素因子筛掉
}
}
}
  • a[] 标记是否为素数,b[] 存储素数列表。
  • 埃氏筛的核心是“用已知素数标记其倍数”,保证效率。

(2)因数个数计算(con 函数)

int con(int x) {
if (x == 1) return 1;
int c = 1;
for (int i = 1; i <= k; i++) {
int p = b[i];
if (p * p > x) break;
// 超过平方根,无需继续
if (x % p == 0) {
int d = 0;
while (x % p == 0) {
// 分解出所有 p 的因子
x /= p;
d++;
}
c *= (d * 2 + 1);
// 原数是平方数,指数翻倍后计算因数个数
}
}
if (x >
1) c *= 3;
// 剩余大素数因子(指数为1,翻倍后指数为2,d=1 → 2*1+1=3)
return c;
}

(3)主函数(solve 函数)

void solve() {
int n, sum = 0;
prime();
// 预处理素数
cin >> n;
vector<
int>
e(n + 1);
for (int i = 1; i <= n; i++) cin >> e[i];
  for (int i = 1; i <= n; i++) {
  int x = e[i];
  int ii = sqrt(x);
  if (ii * ii != x) continue;
  // 不是平方数,直接跳过
  int f = con(ii);
  // 计算平方根的因数个数(即原数的因数个数)
  if (a[f] == 0 && f >
  2) {
  // 因数个数是素数且 ≠2
  sum += x;
  }
  }
  cout << sum << endl;
  }
  • 先筛出平方数(非平方数不可能满足完美遗物条件)。
  • 对平方数,通过 con 计算因数个数,再判断是否为“质数且 ≠2”,若是则累加价值。

AC代码

#include<bits/stdc++.h>
  #define int long long
  using namespace std;
  const int N = 1e6 + 5000;
  int a[N], b[N], k = 0;
  // a数组标记是否为素数,b数组存储素数
  // 埃氏筛法预处理素数
  void prime() {
  a[1] = 1;
  // 1不是素数
  for (int i = 2; i < N; i++) {
  if (a[i] == 0) {
  b[++k] = i;
  // 记录素数
  }
  // 标记非素数
  for (int j = 1; j <= k && i * b[j] < N; j++) {
  a[i * b[j]] = 1;
  if (i % b[j] == 0) break;
  // 优化,避免重复标记
  }
  }
  }
  // 计算x的因数个数相关值(针对平方数的平方根)
  int con(int x) {
  if (x == 1) return 1;
  int c = 1;
  for (int i = 1; i <= k; i++) {
  int p = b[i];
  if (p * p > x) break;
  // 超过平方根,无需继续
  if (x % p == 0) {
  int d = 0;
  // 分解出所有p的因子
  while (x % p == 0) {
  x /= p;
  d++;
  }
  c *= (d * 2 + 1);
  // 计算因数个数
  }
  }
  if (x >
  1) c *= 3;
  // 处理剩余的大素数因子
  return c;
  }
  signed main() {
  prime();
  // 预处理素数
  int n, sum = 0;
  cin >> n;
  vector<
  int>
  e(n + 1);
  for (int i = 1; i <= n; i++) {
  cin >> e[i];
  }
  for (int i = 1; i <= n; i++) {
  int x = e[i];
  int ii = sqrt(x);
  // 先判断是否为平方数(非平方数不可能是完美遗物)
  if (ii * ii != x) continue;
  // 计算因数个数
  int f = con(ii);
  // 判断因数个数是否为素数且不等于2
  if (a[f] == 0 && f >
  2) {
  sum += x;
  }
  }
  cout << sum << endl;
  return 0;
  }
posted @ 2025-08-11 12:56  yfceshi  阅读(8)  评论(0)    收藏  举报