河南萌新联赛2025第一场-河南工业大学
L-Where is zero?
这道题是签到题,随便把一个数变成0即可。
// Problem: Where is zero?
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/113664/L
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define PII pair<int,int>
#define fi first
#define se second
const int N = 1e6+10;
int a[N];void solve()
{int n;cin>>n;for(int i=0;i<n;i++) cin>>a[i];a[2] = 0;for(int i=0;i<n;i++) cout<<a[i]<<' ';
}signed main()
{IOSint T=1;
// cin>>T;while(T--) solve(); return 0;
}
C-富有的国王
这道题一开始还以为要建图,可是看样例就会发现直接让最大的总数减去现在已经有的数量即可(因为给出的铁路不会重复)。
// Problem: 富有的国王
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/113664/C
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define PII pair<int,int>
#define fi first
#define se secondvoid solve()
{int n,m;cin>>n>>m;int sum = n*(n-1);cout<<sum-m<<endl;
}signed main()
{IOSint T=1;
// cin>>T;while(T--) solve(); return 0;
}
H-黑客帝国
这道题是之前寒训的一道蛇形矩阵的题目,这次比赛竟然出来原题那我就不客气了。
只需要用一个变量来控制方向模拟即可,没什么说的,相信看到这篇博客的小伙伴不单单是因为这道题而看的~
// Problem: 黑客帝国
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/113664/H
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define PII pair<int,int>
#define fi first
#define se second
const int N = 1010;
int mp[N][N];
int n;void solve()
{memset(mp,-1,sizeof(mp));cin>>n;int x,y;if(n&1) x=(n+1)/2;else x=n/2;y=x;mp[x][y]=0;int sum = n*n;int f=1;for(int index=1;index<sum;index++){if(f==1){mp[x][++y]=index;if(mp[x+1][y]==-1) f=2;continue;}if(f==2){mp[++x][y]=index;if(mp[x][y-1]==-1) f=3;continue;}if(f==3){mp[x][--y]=index;if(mp[x-1][y]==-1) f=4;continue;}if(f==4){mp[--x][y]=index;if(mp[x][y+1]==-1) f=1;continue;}}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)cout<<mp[i][j]<<' ';cout<<endl;}
}signed main()
{IOSint T=1;cin>>T;while(T--) solve(); return 0;
}
I-二进制转化
- 子串的本质:在二进制字符串中,"01" 和 "10" 子串的数量差异实际上反映了字符串中相邻字符变化的次数。
- 每次从 '0' 变为 '1' 会增加一个 "01" 子串。
- 每次从 '1' 变为 '0' 会增加一个 "10" 子串。
- 首尾字符的影响:
- 如果首尾字符相同(例如
s[0] = '0'
且s[n-1] = '0'
),那么整个字符串的变化次数是偶数次。 - 如果首尾字符不同(例如
s[0] = '0'
且s[n-1] = '1'
),那么整个字符串的变化次数是奇数次。
- 如果首尾字符相同(例如
所以:
- 首尾相同的情况:
- 假设字符串的首尾字符都是 '0'。
- 由于每次变化都是从 '0' 到 '1' 或从 '1' 到 '0',首尾相同意味着变化次数是偶数次。
- 因此,"01" 和 "10" 子串的数量会相等。
- 无论如何选择区间 ([l, r]),我们都可以通过翻转区间内的字符来调整变化次数,使得 "01" 和 "10" 子串的数量相等。
- 首尾不同的情况:
- 假设字符串的首尾字符分别是 '0' 和 '1'。
- 首尾不同意味着变化次数是奇数次。
- 无论如何选择区间 ([l, r]),翻转后的变化次数仍然是奇数次,因此 "01" 和 "10" 子串的数量无法相等。
理清题目意思的本质是很重要的!
// Problem: 二进制转化
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/113664/I
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define PII pair<int,int>
#define fi first
#define se secondvoid solve()
{int n, l, r;string s;cin>>n>>s>>l>>r;if (s[0] == s[s.size()-1]){cout << "Yes" << endl;return;} else if (l == 1 || r == n){cout << "Yes" << endl;return;}cout << "No" << endl;return;
}signed main()
{IOSint T=1;cin>>T;while(T--) solve(); return 0;
}
G-最大子段和,但是两段
这道题我竟然没注意到题目上就说了是最大子段和,用滑动窗口写发现怎么也过不了,后来才发现是把前缀和数组当成后缀和直接用了!!!铭记!
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5 + 10;
const int INF = 1e18;
int a[N], prefix[N], suffix[N];
int left_max[N], right_max[N];void solve() {int n; cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];prefix[i] = prefix[i - 1] + a[i];}// 计算后缀和for (int i = n; i >= 1; i--) {suffix[i] = suffix[i + 1] + a[i];}// 正向单调队列处理左半部分deque<int> q_left;q_left.push_back(0); // 初始放入prefix[0]left_max[0] = -INF;for (int i = 1; i <= n; i++) {// 维护单调递增队列while (!q_left.empty() && prefix[q_left.back()] > prefix[i]) {q_left.pop_back();}left_max[i] = max(left_max[i - 1], prefix[i] - prefix[q_left.front()]);q_left.push_back(i);}// 逆向单调队列处理右半部分deque<int> q_right;q_right.push_back(n + 1); // 初始放入suffix[n+1]=0right_max[n + 1] = -INF;for (int i = n; i >= 1; i--) {// 维护单调递增队列while (!q_right.empty() && suffix[q_right.back()] > suffix[i]) {q_right.pop_back();}right_max[i] = max(right_max[i + 1], suffix[i] - suffix[q_right.front()]);q_right.push_back(i);}// 计算最大两段和int ans = -INF;for (int i = 1; i < n; i++) {ans = max(ans, left_max[i] + right_max[i + 1]);}cout << ans << endl;
}signed main() {ios::sync_with_stdio(0);cin.tie(0);int T; cin >> T;while (T--) solve();return 0;
}
当然用前缀和+单调队列+后缀和+单调队列确实有点麻烦了,下次见到最大子段和可以用动态规划的思想来解决!!!
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define PII pair<int,int>
#define fi first
#define se second
const int N = 5e5 + 10;
int a[N], sum[N];
int l[N], r[N];
int n;void solve() {cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];sum[i] = sum[i - 1] + a[i];}int current_max = a[1];//以当前元素结尾子串的最优解l[1] = a[1];//当前元素之前的最优解for (int i = 2; i <= n; i++) {current_max = max(a[i], current_max + a[i]);l[i] = max(l[i - 1], current_max);}current_max = a[n];r[n] = a[n];for (int i = n - 1; i >= 1; i--) {current_max = max(a[i], current_max + a[i]);r[i] = max(r[i + 1], current_max);}int ans = -1e18;for (int i = 1; i < n; i++) {ans = max(ans, l[i] + r[i + 1]);}cout << ans << endl;
}signed main() {IOSint T = 1;cin >> T;while (T--) solve();return 0;
}
M-无聊的子序列
暴力枚举长度为 1,2,3,4 的子数组即可。
我们可以通过打表的方式找到一些规律,即:不存在任何一个长度大于等于 5 的子数组满足题目中的限制条件,所以只需要暴力枚举就行了。
注意长度为4的时候,要想不存在非严格递增/减,就要相邻两个为一组同单调性且后面一组的起点更弱。
// Problem: 无聊的子序列
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/113664/M
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define PII pair<int,int>
#define fi first
#define se second
const int N = 1e6+10;
int a[N];
int n;
void solve()
{cin >> n;for (int i = 1; i <= n; i++) cin >> a[i];if (n == 1){cout << 1 << endl;return;}//长度为1的子数组有n个,长度为2的子数组有n-1个这两种所有的都满足条件 所以只需要考虑长度为3和4的即可int ans = n * 2 - 1;//暴力长度为3的子数组for (int i = 1; i <= n - 2; i++){if (a[i] <= a[i + 1] && a[i + 1] <= a[i + 2]) continue;if (a[i] >= a[i + 1] && a[i + 1] >= a[i + 2]) continue;if (a[i] == a[i + 1] || a[i + 1] == a[i + 2]) continue;ans++;}//暴力长度为4的子数组for(int i = 1; i <= n-3; i++){//长度为4时只有两个递增或者两个递减if (a[i] > a[i + 1] && a[i] < a[i + 2] && a[i + 3] > a[i + 1] && a[i + 3] < a[i + 2]) ans++;if (a[i] < a[i + 1] && a[i] > a[i + 2] && a[i + 3] < a[i + 1] && a[i + 3] > a[i + 2]) ans++;}cout << ans << endl;
}signed main()
{IOSint T=1;
// cin>>T;while(T--) solve(); return 0;
}
B-代价转移
这个题目可以用优先队列存当前的最小代价,不断的用最小代价的点来搜索,直到达到了这个点
代码如下:
// Problem: 代价转移
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/113664/B
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define PII pair<int,int>
#define fi first
#define se second
const int N = 1e3+10;
int a[N];
int n,m,c1,c2,c3;void solve()
{cin>>n>>m>>c1>>c2>>c3;if(n==m){cout<<0<<endl;return ;}memset(a,0x3f3f3f,sizeof a);priority_queue<PII,vector<PII>,greater<PII>> q;//PII中第一个值为目前当前数值的代价 第二个值为当前的数值q.push({0,n});while(!q.empty()){PII t = q.top();q.pop();if(t.se==m){cout<<t.fi<<endl;return ;}if(t.fi>a[t.se]) continue;if(t.se+1<N && t.fi+c1<a[t.se+1]){a[t.se+1]=t.fi+c1;q.push({a[t.se+1],t.se+1});}if(t.se-1>0 && t.fi+c2<a[t.se-1]){a[t.se-1]=t.fi+c2;q.push({a[t.se-1],t.se-1});}if(t.se*2<N && t.fi+c3<a[t.se*2]){a[t.se*2]=t.fi+c3;q.push({a[t.se*2],t.se*2});}}
}signed main()
{IOSint T=1;cin>>T;while(T--) solve(); return 0;
}
详解见:超详细的代码
A-隔板
这道题算是一个较难的题目了,因为这类问题有好多种,分为好几种问题,需要取花费大量时间取整理。
简单总结一下四种排列组合的递推式:
小球相同盒子相同:
f(n, m) = f(n-1, m-1) + f(n-m, m)
边界条件:
f(n,1) = 1;
f(n,n) = 1;
f(n,m)当n<m时等于0:
小球相同盒子不同时:
int qpow(int a, int b)
{a %= mod;int ans = 1;while(b) {if(b%2 == 1)ans = (ans*a)%mod;a=(a*a)%mod;b/=2;}return ans;
}
// 计算组合数 C(n, m) 对 mod 取模的结果
int zhs(int n, int m)
{int f1[n+1], f2[n+1];f1[0] = 1;//特判一下,0的阶乘是1for (int i = 1; i <= n; i++)f1[i] = (f1[i-1]*i)%mod; // 计算阶乘数组并取模f2[n] = qpow(f1[n], mod-2); // 计算阶乘的逆元for (int i = n-1; i>=0; i--)f2[i] = (f2[i+1]*(i+1))%mod; // 计算逆元数组int ans;ans = (f1[n]*f2[m]%mod*f2[n-m]) % mod;return ans;
}
小球不同盒子相同时:
需要用到 第二类斯特林数
递推公式:f(n, m) =f(n-1, m-1) + m·f(n-1, m)
最终答案就为 ans = dp[n][m] % mod;
dp[0][0]=1; // 边界条件:f(0,0)=1for(int i=1; i<N; ++i) {for(int m=1; m<=i; ++m) // m最多为i(否则f(i,m)=0)dp[i][m] = (dp[i-1][m-1]+m*dp[i-1][m])%M;}
小球不同盒子也不同时:
利用斯特林数(第二类)和阶乘的组合来解决
斯特林数的预处理:f(n, m) = f(n-1, m-1) + m·f(n-1, m)
边界条件:f(0,0) = 1,n<m 时 f(n,m)=0
这道题显然属于第四种:
// Problem: ¸ô°å
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/113664/A
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define PII pair<int,int>
#define fi first
#define se second
const int N = 5e3+10;
int n,m;
const int mod = 998244353;
int dp[N][N];//第二类斯特林数
int jie[N];//阶乘void solve()
{cin>>n>>m;if(n<m){cout<<0<<endl;return ;}jie[0]=1;//阶乘算到盘子即可 因为阶乘是由盘子的不同排列引起的for(int i=1;i<=m;i++) jie[i] = i % mod * jie[i-1] % mod;dp[0][0]=1;//初始化斯特林数 f(0,0)=1;for(int i=1;i<=n;i++)for(int j=1;j<=i;j++)dp[i][j] = (dp[i-1][j-1] + j*dp[i-1][j]) % mod;cout<<dp[n][m] % mod * jie[m] % mod;
}signed main()
{IOSint T=1;
// cin>>T;while(T--) solve(); return 0;
}
更多题解:题解