2026萌新联赛第三场-- (郑州轻工业大学)
前言
真不能闹了,这次比赛做的一塌糊涂啊,看着这个榜单就知道题目不是很难,可是做起来又是一个情况。为什么急躁,为什么死磕,这都是问题啊,由于A题目就是过不了,导致后面有一个基本算是原题都没有做出来,真是没话说了。
全排序那一题本来我看数据才11,那我就用我记得一个公式来做吧,但是谁知道11也会卡TLE,但凡是10就不会卡,真服了,这题不会dfs,看着它过题人数越来越多啊。
本场题目出题人没有给出题范围,那么我就自己来打一下吧
easy H J
easy mid A I G B
mid C E(会板子就只能排在easy mid) F
hard D K
题目链接:(0条未读通知) 河南萌新联赛2026第(三)场:郑州轻工业大学_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ
感兴趣得可以去看一下
H题
H-wnd的魔法背包_河南萌新联赛2026第(三)场:郑州轻工业大学
这个题目可以说很水了,读完题就知道怎么写了。
就是一个向下取整,然后把整数累加,负数从小到大变成正数,注意次数就行了,没什么好说的
AC代码
cpp
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl '\n'
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
#define YES cout<<"YES"<<endl;
#define NO cout<<"NO"<<endl;
const int INF=1e6+5;
void solve()
{
int n;
cin>>n;
vector<int> a;
int ans=0;
int shu=n/2;
for(int i=0;i<n;i++)
{
int num;
cin>>num;
if(num>0) ans+=num;
else a.push_back(num);
}
sort(a.begin(),a.end());
int len=a.size();
shu=min(shu,len);
for(int i=0;i<shu;i++)
{
ans+=(-1*a[i]);
}
cout<<ans<<endl;
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
// cin>>_;
while(_--)
solve();
return 0;
}
J题
J-魔法数字_河南萌新联赛2026第(三)场:郑州轻工业大学
思路:这题也很水的题,不过赛时一直在卡TLE,我在想,这数字最多进行三次循环怎么会TLE呢,于是我赛时就改了一下代码浪费了我大量时间,在赛后发现原来最开始的代码有两个地方写错误导致死循环了。不说了,上代码
AC代码
cpp
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl '\n'
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
#define YES cout<<"YES"<<endl;
#define NO cout<<"NO"<<endl;
const int INF=1e6+5;
void solve()
{
string s;
cin>>s;
int num=0;
if(s.size()==1){
cout<<0<<' '<<s[0]<<endl;
return;
}
int len=s.size();
for(int i=0;i<len;i++)
{
num+=(s[i]-'0');
}
int cnt=1;
if(num<10){
cout<<1<<' '<<num<<endl;
return;
}
// cout<<num<<endl;
int ans=0;
while(1)
{
int shu=num%10;
ans+=shu;
num/=10;
if(num==0) cnt++;
if(num==0&&ans<10){
cout<<cnt<<' '<<ans<<endl;
return;
}else if(num==0&&ans>=10){
num=ans;
ans=0;
}
}
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
cin>>_;
while(_--)
solve();
return 0;
}
A题
A-能量任务_河南萌新联赛2026第(三)场:郑州轻工业大学
这个题我说实话,也很简单,你只要把这个贪心思路想出来,把这个排序排出来之后就变得十分好写。
这里有两个方法来写,第一种正常全部数进行排序,我们就根据h从小到大排序,但是如果遇到了d小于0的情况我们得另外判断,因为d小于0相当于如果我们做完这个任务我们的能量不会增加而是会减少,所以这时候我们就看这两个比较的数的d值,怎么排序呢?如果两个数的d都是负数,我们就按照他们hd的和的值,按照从大到小排序,要是一正一负那我们肯定想要增加能量而不是减少,所以按照d从大到小排序。就根据这个排序然后我们再进行二分尝试一个最小能量就行了。
另外一个方法是什么呢?那就是我们按照d的正负来分别排序,正数一个数组,负数一个数组,然后正数按照h从小到大排序,负数那个数组我们还是按照h+d的和从大到小排序,然后这两个数组都排完序了,我们先便利正数数组,然后再遍历负数数组,进行二分尝试,找一个最小的值。
哦,对了,忘记解释二为什么不按照正规排序也可以了,由于我们不想让能量降低,那么干脆就先把增加的能量过完了再进行降低能量的任务,因为就算我这个任务先做了,那么可能到后面这个能量就到不了下一个的门槛,但是由于没有做这个减少能量的任务,我们可以做下一个增加能量的任务。这就方便的多,先找一个能通过所有的正数的最小值,然后看看这个最小值在负数那里面符不符合,不符合我们再在这个最小值的基础上找。
AC代码(法1)
cpp
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl '\n'
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
#define YES cout<<"YES"<<endl;
#define NO cout<<"NO"<<endl;
const int INF=1e6+5;
int ksm(int a, int b, int p) {
int res = 1;
while (b) {
if (b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
struct node{
int h,d;
int id;
};
bool cmp(node a,node b)
{
if(a.d<0||b.d<0)
{
if(a.d==b.d)return a.h>b.h;
else if(a.d<0&&b.d<0)
{
int c=a.h+a.d;
int d=b.h+b.d;
return c>d;
}
else return a.d>b.d;
}
return a.h<b.h;
}
void solve()
{
int n;
cin>>n;
vector<node> a(n);
for(int i=0;i<n;i++) {
cin>>a[i].h>>a[i].d;
}
sort(a.begin(),a.end(),cmp);
int l=0,r=LLONG_MAX;
int ans=-1;
while(l<=r)
{
int mid=(l+r)/2;
int num=mid;
bool ok=true;
for(int i=0;i<n;i++)
{
if(num<a[i].h){
ok=false;
break;
}else{
num+=a[i].d;
}
}
if(!ok) {
l=mid+1;
}
else{
r=mid-1;
ans=mid;
}
}
cout<<ans<<endl;
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
// cin>>_;
while(_--)
solve();
return 0;
}
AC代码(法2)
cpp
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl '\n'
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
#define YES cout<<"YES"<<endl;
#define NO cout<<"NO"<<endl;
const int INF=1e6+5;
int n;
vector<pii> a1;
vector<pii> a2;
vector<pii> arr;
bool cmp1(pii a,pii b)
{
if(a.fi==b.fi) return a.se>b.se;
return a.fi<b.fi;
}
bool cmp2(pii a,pii b)
{
return a.fi+a.se>b.fi+b.se;
}
bool check(int x)
{
for(int i=0;i<n;i++)
{
if(x<arr[i].fi){
return false;
}else{
x+=arr[i].se;
}
}
return true;
}
void solve()
{
cin>>n;
for(int i=0;i<n;i++) {
int num1,num2;
cin>>num1>>num2;
if(num2>=0){
a1.push_back({num1,num2});
}else{
a2.push_back({num1,num2});
}
}
sort(a1.begin(),a1.end(),cmp1);
sort(a2.begin(),a2.end(),cmp2);
int len1=a1.size();
int len2=a2.size();
for(int i=0;i<len1;i++) arr.push_back({a1[i].fi,a1[i].se});
for(int i=0;i<len2;i++) arr.push_back({a2[i].fi,a2[i].se});
int l=0,r=LLONG_MAX;
int ans=LLONG_MAX;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid)){
r=mid-1;
ans=mid;
}else{
l=mid+1;
}
}
cout<<ans<<endl;
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
// cin>>_;
while(_--)
solve();
return 0;
}
G题
G-wnd的魔法排序_河南萌新联赛2026第(三)场:郑州轻工业大学
思路:本题是有一个全排列公式的,但是我在赛时是试了的,如果n等于10的话就恰好能过,但是等于11可能就差点,那么如果遇到n很小的时候我们可以用全排列公式next_permutation
那如果用不了公式怎么办呢?那我们就想全排列的底层逻辑啊,那不就是选择一个数然后再选择剩下的数,然后一层一层的选择数字,那不就是dfs递归嘛,可是赛时dfs就是不会写,没办法,赛时只能干着急,只能赛后补了-_- 这个dfs其实是很好写的,就通过一层一层递归定义一个标记数组,表示这个数字被选择了没有,然后我们在选择数字的过程中进行判断数字合不合理,也就是相邻两个数字和得是质数,然后如果途中这个选择就不行我们就直接跳过走下一步,这里就是为什么全排列TLE的原因,因为全排列会把所有的情况列出来再判断,而我们dfs可以在n个数字形成之前就pass一些不可以的数字,节省了一点时间。
然后我们定义一个记录数量的,这个数量等于n我们就把这个数组输出出来然后回退到上一层接着遍历,注意的是我们回退之后这个数字我们得把它重新标记为没用过(也就是回溯)。
AC代码
cpp
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl '\n'
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
#define YES cout<<"YES"<<endl;
#define NO cout<<"NO"<<endl;
const int INF=1e6+5;
int n;
int a[30];
int vis[30]={0};
int ans[30];
bool check(int x)
{
if(x==2) return true;
for(int i=2;i<=sqrt(x);i++)
{
if(x%i==0) return false;
}
return true;
}
void dfs(int step)
{
if(step==n)
{
for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
cout<<endl;
return;
}
for(int i=1;i<=n;i++)
{
if(vis[i]) continue;
if(step==0||check(ans[step]+i)) ans[step+1]=i;
else continue;
vis[i]=1;
dfs(step+1);
vis[i]=0;
}
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++) a[i]=i;
dfs(0);
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
// cin>>_;
while(_--)
solve();
return 0;
}
I题
I-遗迹核心的临界容差_河南萌新联赛2026第(三)场:郑州轻工业大学
这个题就是一个很简单并且很板子的二分答案题目啊,我们通过二分尝试,然后取最小值就行了。其实没什么好说的,当时开的人少加上自己死磕A题就没看这题,就一个板子二分
AC代码
cpp
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl '\n'
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
#define YES cout<<"YES"<<endl;
#define NO cout<<"NO"<<endl;
const int INF=1e9+5;
int n,k;
vector<int> a;
vector<int> cha;
bool check(int mid)
{
int cnt=0;
for(int i=0;i<n-1;i++)
{
if(cha[i]>mid) cnt++;
}
return cnt<k;
}
void solve()
{
cin>>n>>k;
a.resize(n);
cha.resize(n);
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n-1;i++) cha[i]=abs(a[i+1]-a[i]);
int l=0,r=INF;
int ans=INF;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid)){
r=mid-1;
ans=mid;
}else{
l=mid+1;
}
}
cout<<ans<<endl;
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
// cin>>_;
while(_--)
solve();
return 0;
}
B题
B-三色平衡_河南萌新联赛2026第(三)场:郑州轻工业大学
思路:这个题我们可以通过数学思维,用前缀和加哈希来做
首先题目要求的我们转化一个含义就可以表示为一段区间
cnt0=cnt1=cnt2
那么我们把这个式子转化一下变成cnt0-cnt1=cnt0-cnt2
当然也可以写成cnt0-cnt1=cnt2-cnt1,这些都无所谓,我们只是想要一个式子,那么我们哈希表里面寻访的是前缀我们遇到的cnt0-cnt1,cnt0-cnt2两个差值的最远的一个下标,然后如果后面我们遇到了这个相等的差值,就表示中间的012的个数相等才会出现一样的cnt0-cnt1,cnt0-cnt2,那么我们这时候不更新哈希,只能新长度就行了
AC代码
cpp
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl '\n'
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
#define YES cout<<"YES"<<endl;
#define NO cout<<"NO"<<endl;
const int INF=1e6+5;
map<pii,int> mp;
void solve()
{
int n;
cin>>n;
string s;
cin>>s;
int cnt0=0,cnt1=0,cnt2=0;
int ans=0;
mp[{0,0}]=0;
for(int i=0;i<n;i++)
{
if(s[i]=='0') cnt0++;
if(s[i]=='1') cnt1++;
if(s[i]=='2') cnt2++;
pii t={cnt0-cnt1,cnt0-cnt2};
if(mp.count(t)) ans=max(ans,i+1-mp[t]);
else mp[t]=i+1;
}
cout<<ans<<endl;
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
// cin>>_;
while(_--)
solve();
return 0;
}
E题
思路:这个题其实也不难,就是一个并查集的应用,找所有弱连通分量中节点数的最大值。根据题目的意思来讲,我们可以不考虑方向问题,直接定义一个人当"老大",然后找每个连通分量的长度,我们取最大值就行了。并查集怎么用的话,怎么说呢?感觉这题比较简单吧,我们肯定首先定义自己是自己的老大,然后合并u,v,任意让谁当老大,v当就行,然后只要find函数找两个人的老大,要是不是一个人我们就把他们合并,然后把它的小弟加上就行了,具体看代码就懂了。
AC代码
cpp
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl '\n'
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
#define YES cout<<"YES"<<endl;
#define NO cout<<"NO"<<endl;
const int INF=1e6+5;
int fa[INF];
int sz[INF];
int find(int x)
{
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
void unite(int u,int v)
{
u=find(u);
v=find(v);
if(u!=v)
{
fa[u]=v;
sz[v]+=sz[u];
}
}
void solve()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
fa[i]=i;
sz[i]=1;
}
for(int i=1;i<=n;i++){
int p;
cin>>p;
unite(i,p);
}
int ans=0;
for(int i=1;i<=n;i++)
{
if(find(i)==i) ans=max(ans,sz[i]);
}
cout<<ans<<endl;
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
cin>>_;
while(_--)
solve();
return 0;
}
C题
C-余数清理_河南萌新联赛2026第(三)场:郑州轻工业大学
这个题涉及到"同余定理"的数论知识,如果理解了这个知识点我们就很好写了,这个结论首先给出,下面会有解释和推论。
就是
p ( l − 1 ) = ( p r − t a r g e t + m ) m o d m p(l-1)=(pr-target+m)modm p(l−1)=(pr−target+m)modm
其中呢,我们的p数组是前缀和对m取余的结果,这个target就等于pn,然后我们遍历数组,这个指针指的是pr,那么这时候的pl-1就可以表示出来了,也是我们需要查找的值,在我们i左边遍历的过程中看看有没有出现过 (pr - target + m) mod m
出现过我们就计算这个长度并且更新这个出现的位置为右边的,如果没有找到就只更新这个出现位置。
这个查找过程我们需要用到哈希来实现。
下面我们讲解一下这个上面推论公式是如何得来的。如图
如果这个结论理解了,那么我们再回到思路来看看是否理解如何做。
AC代码
cpp
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl '\n'
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
#define YES cout<<"YES"<<endl;
#define NO cout<<"NO"<<endl;
const int INF=1e6+5;
void solve()
{
int n,m;
cin>>n>>m;
vector<int> a(n+1);
vector<int> p(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
p[1]=a[1]%m;
for(int i=2;i<=n;i++) p[i]=(p[i-1]+a[i])%m;
vector<int> vis(m,-1);
int ans=INF;
int target=p[n];
vis[0]=0;
for(int i=1;i<=n;i++)
{
int shu=(p[i]-target+m)%m;
if(vis[shu]!=-1&&i-vis[shu]<n) ans=min(ans,i-vis[shu]);
vis[p[i]]=i;
}
if(ans>n) cout<<-1<<endl;
else cout<<n-ans<<endl;
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
// cin>>_;
while(_--)
solve();
return 0;
}
总结
这场比赛只能说一言难尽,最后吟诗一首歌颂我逝去的比赛
鏖罢方知梦未安,
功亏一瞬意难宽。
一腔愤懑摧胸臆,
半卷烽烟染泪澜。
莫叹今朝锋铩羽,
当磨来日剑重弹。
埋愁且蓄凌云志,
再赴征场破万难。
