2026河南萌新联赛第四场--南阳理工学院
前言
这场也是颇为丰富的一场比赛啊,很多简单板子题,数学题也很多。特别是最后一题,纯高数问题!!!
其他的有一个简单的01背包,基本还是板子题,知识有一点变种,然后还有变种bfs,其实能写的那几道都还行,难度都不大,其他的就比较考察算法了。
我得注重dp了,但是这玩意太烧脑了,赛时压根想不起来,一个简单的01背包我在赛事也是乱写一番啊。
只能说这场难度是有的,能写的大概是CDGHJLM(对于我现在来说)IIII I也能写
不是这个I也太那个了吧,好吧,这个I是我眼拙,它榜歪了!
我这里的H没有补,以后学精透了我再来补充
好吧,这个H题看着吓人,实际上这个dp也不是很难吧,特此补题!
M题
M-噢耶,奶奶的龙!!!_河南萌新联赛2026第(四)场:南阳理工学院
纯高数问题,最后一项答案是0,所以答案0
php
0
L题
L-A × B_河南萌新联赛2026第(四)场:南阳理工学院
高精度乘法模板
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 s1;
string s2;
cin>>s1>>s2;
int n=s1.size(),m=s2.size();
vector<int> ans(n+m,0);
reverse(s1.begin(),s1.end());
reverse(s2.begin(),s2.end());
for(int i=0;i<n;i++) {
for (int j=0;j<m;j++) {
ans[i+j]+=(s1[i]-'0')*(s2[j]-'0');
}
}
for(int i=0;i<n+m-1;i++) {
ans[i+1]+=ans[i]/10;
ans[i]%=10;
}
while(ans.size()>1&&ans.back()==0) ans.pop_back();
for(int i=ans.size()-1;i>=0;i--){
cout<<ans[i];
}
cout<<endl;
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
// cin>>_;
while(_--)
solve();
return 0;
}
G题
G-你逃不过我你信不信_河南萌新联赛2026第(四)场:南阳理工学院
思路题一道啊,看到这题第一感觉就是打表,果不其然,规律出来了,在n等于20以内没啥规律,我们可以用代码输出,在20以后答案都是0313
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;
}
void solve()
{
int n;
cin>>n;
int shu=1;
int ans=1;
if(n<=20){
for(int i=2;i<=n;i++)
{
if(shu>10000) shu=shu%10000;
ans*=i;
if(ans>10000) ans=ans%10000;
shu+=ans;
}
int num=shu%10000;
int wei=0;
int num1=num;
while(num1)
{
num1/=10;
wei++;
}
for(int i=0;i<4-wei;i++) cout<<0;
cout<<num<<endl;
}else{
cout<<"0313"<<endl;
}
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
// cin>>_;
while(_--)
solve();
return 0;
}
J题
J-小苯的星轨_河南萌新联赛2026第(四)场:南阳理工学院
这题不还是一个数学题嘛,问你两个点构成的线过不过原点,那我们只需要存斜率比较斜率就行了啊。但是值得注意的是我们的斜率得用数字来存,并且得存的是比值的约分之后的结果。赛时我一直在算double,发现有误差一直过不了,所以这里我们可以改成pii存分子分母,存之前用gcd先约分一下就行。
cpp
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
int zuhe(int x)
{
return x*(x-1)/2;
}
void solve()
{
int n;
cin >> n;
vector<int>x(n+1,0);
vector<int>y(n+1,0);
map<pii,int>mp;
int ans=0;
for(int i=1;i<=n;i++)
{
cin >> x[i] >> y[i];
if(x[i]==0&&y[i]==0)
{
ans+=n-1;
}
else
{
int g=__gcd(x[i],y[i]);
mp[{x[i]/g,y[i]/g}]++;
}
}
for(auto &x:mp)
{
ans+=zuhe(x.se);
}
cout << ans << endl;
}
signed main()
{
IOS;
int T = 1;
cin >> T;
while(T --) solve();
return 0;
}
D题
D-小圆爱玩龙龙(Easy)_河南萌新联赛2026第(四)场:南阳理工学院
经典的01背包问题,由于只能选择一次,所以我们肯定循环是倒着算的
状态转移方程:
cpp
dp[i]=max(dp[i],dp[j-X]+Y);
那么我们肯定面临的是选与不选,还有一个免费的,那么我们就再遍历一层循环表示第几个是免费的,然后我们的dp到达这里就跳过,剩下就是经典的01背包模板,第二层循环倒序进行遍历
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;
}
void solve()
{
int n,W;
cin>>n>>W;
vector<int> w(n);
vector<int> v(n);
for(int i=0;i<n;i++) cin>>w[i]>>v[i];
int ans=0;
for(int f=0;f<n;f++)
{
vector<int> dp(W+1,0);
for(int i=0;i<n;i++)
{
if(i==f) continue;
for(int j=W;j>=w[i];j--)
{
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
ans=max(ans,dp[W]+v[f]);
}
cout<<ans<<endl;
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
// cin>>_;
while(_--)
solve();
return 0;
}
C题
C-重返首发地 (Return to the Origin)_河南萌新联赛2026第(四)场:南阳理工学院
这题是一个搜索问题,我们可以观察到,题目让我们找的是每一个点找到一个P的最近路径,而这题的P是不止一个的,所以正常遍历bfs是肯定不行的,那么我们可以反过来想,我们想要每一个同学都找到P,那么我们可以反过来用P来找其他人的最小路径
那么由于是多个P,所以我们从所有的P一起除法,每一个点谁先到它就是最小的路径。而题目还有一个花费问题,这也是我们走路径关键,有0走0,#不能走,1实在没办法就走,然后如果我们走了一个权值是0的路径,并且这个路径是比之前更加短的那么它的下一个不经肯定是还没走,那么我们肯定是想要走更短的,所以这时候我们想要"插队",就得放到队头。所以这时候我们需要一个双端队列来实现。
详细看代码
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;
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
void solve()
{
int n,m;
cin>>n>>m;
vector<string> s(n);
for(int i=0;i<n;i++) cin>>s[i];
vector<vector<int>> dist(n,vector<int>(m,INF));//路径初始化为INF
vector<vector<bool>> vis(n,vector<bool>(m,false));//标记路径
deque<pii> dq;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(s[i][j]=='P'){
dist[i][j]=0;//P点我们的路径肯定是0
dq.push_back({i,j});
}
}
}
while(!dq.empty()){
auto [x,y]=dq.front();
dq.pop_front();
if(vis[x][y]) continue;//走过跳过
vis[x][y]=true;
int w=((s[x][y]=='1')?1:0);
for(int i=0;i<4;i++)
{
int nx=x+dx[i];
int ny=y+dy[i];
if(nx<0||nx>=n||ny<0||ny>=m) continue;
if(s[nx][ny]=='#') continue;
if(dist[nx][ny]>dist[x][y]+w){
dist[nx][ny]=dist[x][y]+w;
if(w==0) dq.push_front({nx,ny});
else dq.push_back({nx,ny});
}
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(dist[i][j]==INF) cout<<-1<<' ';
else cout<<dist[i][j]<<' ';
}
cout<<endl;
}
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
cin>>_;
while(_--)
solve();
return 0;
}
I题
I-小苯的括号高度_河南萌新联赛2026第(四)场:南阳理工学院
恕我眼拙,这题不涉及任何算法思想,是一个纯纯的数学逻辑加贪心思想的题目,特来补题
首先读完题目我们知道啊,这个k肯定是在<n或者>n*n的时候是不成立的,这个是毋庸置疑的那么我们通过一顿分析,发现这个(n-k)这个数对2取余,如果是1,那还是不可能,得输出-1。具体推论看下图

既然这个推论出来了,那么我们就可以判断出来三条特判
1: k < n 输出 -1
2:k > n×n 输出 -1
3:(k-n)%2==1 输出 -1
那么剩下就只剩构造了
那么我们的构造又有什么方法呢?
首先我们知道并排是只有n的贡献,那么还差n-k个贡献,而这个贡献需要我们进行嵌套才能实现,那么怎么嵌套呢?我们可以考虑到,遍历到后面的()无非只有两种选择
1:嵌套前面的所有
2:和前面的并列
那么我们考虑到,如果嵌套前面的,那么我们的额外贡献加多少呢?首先我们知道这个括号对是第i个,那么前面已经有i-1个括号对了,由于我们前面所分析的,如果这个括号嵌套前面的,我们可以,哦,抱歉刚发现前面好像并没有分析,那么我们现在分析一下啊:此时有两种操作:
- 直接放在末尾(并列) :
新串 = 旧串 +()
这对新括号对总高度贡献是 1(就是它自己的基础贡献),额外半贡献为 0。- 包在整个旧串外面 :
新串 =(+ 旧串 +)
这对新括号把之前所有的i-1对括号都包了进去。
- 旧串的每个字符的高度都会 +1,旧串长度为
2*(i-1),所以总高度增加了2*(i-1)。- 转换成半贡献,就是
i-1。所以,第
i对括号如果选择"包外面",它能提供的半贡献就是i-1。
有人问半贡献是啥?每一对
()本身至少占 1 个单位的高度。所以n对括号,最低高度是n(全部并排)当你把一对新括号 包在外面 时,它不会只增加 1 个高度。因为它把里面所有的括号都"抬高了一层"。
因为每次"包在外面"带来的增量都是 偶数 (
2x)。既然是偶数,我们就可以把它 对半砍 ,只记录里面的x是多少。
- 这个
x,就是所谓的 "半贡献"。- 题目里的
need = (k - n) / 2,就是我们需要凑出来的所有"半贡献"的总和。
所以对于每一个括号我们进行选择嵌套不嵌套的选择,我们肯定是这样凑到(k-n)/2个半贡献,就是从最后一个括号开始遍历,看看他需不需要嵌套括号,然后要是嵌套括号的话它会减少一定的半贡献,然后接着遍历,知道(k-n)/2==0就结束,我们对于需要嵌套的进行标记就行了
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;
// bool vis[INF]={false};
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;
}
void solve()
{
int n,k;
cin>>n>>k;
if(k<n||k>n*n){
cout<<-1<<endl;
return;
}
if((k-n)%2!=0){
cout<<-1<<endl;
return;
}
vector<bool> vis(n+1,false);
int need=(k-n)/2;
for(int i=n;i>=1;i--)
{
if(need==0) break;
if(i-1<=need){
vis[i]=true;
need-=(i-1);
}
}
deque<char>dq;
for (int i=1;i<=n; i++) {
if(vis[i]){
dq.push_front('(');
dq.push_back(')');
} else {
dq.push_back('(');
dq.push_back(')');
}
}
for(char c:dq) cout<<c;
cout<<endl;
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
cin>>_;
while(_--)
solve();
return 0;
}
有一点值得注意的是,这里我们不能用string,因为一次一次的前加或者后加字符有点耗时间,避免这个时间损耗,我们用双端队列来实现
H题
H-小苯的镜像符文_河南萌新联赛2026第(四)场:南阳理工学院
吓人的H题,自从学长说这题有点难理解之后就没敢碰它,但不信邪的我还是来看了看,发现别有洞天!
思路:首先我们知道题目所说的拼接方法无非只有两个1:通过一个一个拼接,每一个拼接字符有一个价格
2:通过选取一段区间,我们可以直接把这个区间翻转过来补充到串后面,花费k
那么我们分析出来,后面这个方法的不就是回文嘛,而且还是偶数回文,因为问你是复制前面区间,那么无论前面是偶数巨剑还是奇数区间,后面再补充一个那么这个回文肯定是一个偶数区间嘛。
那么我们的解决方法就是正常我们一个一个拼接,然后对于每一个字符,我们可以让它进行两边扩展:
就是说比如我们现在遍历到了字符i,那么我们以这个字符和下一个字符为中心,找字符i的左边的字符和右边的字符相等的长度,也就是我们以i和i+1,为中心进行两边扩展找回文,找到了,我们就比较这一段用方法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=(1LL<<60);
void solve()
{
int n,k;
cin>>n>>k;
string s;
cin>>s;
vector<int> cost(26);
for(int i=0;i<26;i++) cin>>cost[i];
vector<int> dp(n+1,INF);
dp[0]=0;
for(int i=0;i<n;i++){
dp[i+1]=min(dp[i+1],dp[i]+cost[s[i]-'a']);
for(int l=i-1,r=i;l>=0&&r<n&&s[l]==s[r];l--,r++){
dp[r+1]=min(dp[r+1],dp[i]+k);
}
}
cout<<dp[n]<<endl;
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
cin>>_;
while(_--)
solve();
return 0;
}
总算补完题了,这周补题速度有点慢了,马上结束了才补完,庆幸也是完成这项任务了!