河南萌新联赛2025第(七)场:郑州轻工业大学
河南萌新联赛2025第(七)场:郑州轻工业大学
If I only could, I'd be running up that hill!
郑轻有品!背景终于不是二次元了… 是Stranger Things!希望我能像主题曲里那样,勇攀高峰,Running up that hill!
上次说早了,这才是最后一场···总之感谢萌新联赛吧,比赛的质量很高,陪伴和引导了我一个暑假的学习。这场比赛给我的感觉就是对一些基础的东西还要多练。还有就是对一些代码比较复杂的题多花功夫去斟酌,耐下心来去改。K题写的太长了,光改代码就改了两个小时。主要是我之前刷cf遇到了类似的题,我写的思路是学的那道题的题解,没往其他方面想,不然应该也会用较好写的方法吧。
本场链接:河南萌新联赛2025第(七)场:郑州轻工业大学
C、灯光是我在异世界的呼喊
思路
这题就是看能不能想得到,能想到方法还是比较好写,不然可能有些无从下手
- 对所有字符串进行排序,这样可以保证所有前缀相同的密文放到一起,方便后续进行处理。
- 对于每一个前缀向后进行遍历,如果遇到不一样的就更新大小和前缀
代码
// Problem: 灯光是我在异世界的呼喊
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/116435/C
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// Submission Time: 2025-08-27 13:27:34vector<string> v;
string s;
void solve()
{cin >> n;for(int i=1; i<=n; i++){cin >> s;v.push_back(s);}sort(v.begin(),v.end());int ans=0;for(int i=0; i<v.size(); i++){int cnt=0;string s1=v[i];int k=v[i].size();for(int j=i; j<v.size(); j++){if(v[j].substr(0,k)==s1){cnt++;ans=max(ans,cnt);continue;}i=j-1;break;}}cout << ans;
}
D、灯泡
思路
本题1e181e181e18的数据量并且样例很水,显然是有一定规律可以O(1)O(1)O(1)解决,出题人不想直接说明白。令我不理解的是为什么题解是数学公式和二分,感觉不太合常理而且我也没看懂。但是评论区还是有大佬打表找出了O(1)O(1)O(1)解决的方法。规律还是很好看的,我当时的思考点偏了,还以为不同nnn状态下前面灯的状态也会改变···
- 通过打表可以发现1,4,9,16···和2 * 1, 2 * 4, 2 * 9···的灯都是亮的
- 后边的nnn再变不会影响前边灯的状态
- 所以:如果其本身是平方数或者i/2是平方数,那么这个灯就会亮
- 直接输出(int)sqrt(n)+(int)sqrt(n/2)(int)sqrt(n)+(int)sqrt(n/2)(int)sqrt(n)+(int)sqrt(n/2)即可
代码
// Problem: 灯泡
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/116435/D
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)void solve()
{cin >> n;int k=(int)(sqrt(n))+(int)(sqrt(n/2));cout << k;
}
E、最长优美子序列
思路
这题就这个优美子序列的定义之前肯定遇到过一样的,可能具体问的不太一样。本题是一个动态规划,目前还是对它不太熟悉。
- 外层从后往前对字符串进行遍历,内层 i , j 遍历所有可能的字母组合
- 条件判断if(x!=j&&x!=k)确保当前字符 x 不等于 j 和 k,即满足相邻相异和相隔一个相异的条件。
- 更新 dp[j][k]dp[j][k]dp[j][k]如果当前字符 x 可以插入到字符对 (j,k)(j,k)(j,k) 之间,则更新 dp[j][k]dp[j][k]dp[j][k]为dp[x][j]+1dp[x][j] + 1dp[x][j]+1,表示在子序列中插入一个字符 x。
代码
// Problem: 优美子序列
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/116435/E
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)int dp[30][30];//存26个英文字母
void solve()
{int n;cin>>n;string s;cin>>s;int ans = 0;for(int i = n-1;i>=0;i--){int x = s[i]-'a';for(int j = 0;j<=25;j++){for(int k = 0;k<=25;k++){if(x!=j && x!=k){dp[j][k]=max(dp[j][k],dp[x][j]+1);ans = max(ans,dp[j][k]);}}}}cout << ans << endl;
}
J、掷骰子
思路
- 会发现蓝骰子可以通过红骰子+1得到
- 又由于蓝骰子强制掷9次,因此可以转化为红骰子掷18次+9
- 还可以发现红骰子每个数之间都差5,所以不同方案之间的差值一定是5的倍数
- 可以算得方案的下界为2*18+9=45,上界为27 * 18+9=495
- 于是可以得到判断条件:n>=45&&n<=495&&(n-45)%5==0
代码
// Problem: 优美子序列
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/116435/E
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// Submission Time: 2025-08-27 13:09:47bool isa(int x)
{if(x<45||x>495) return 1;if((x-45)%5!=0) return 1;return 0;
}
void solve()
{cin >> n;int cnt=0;for(int i=1; i<=n; i++) cin >> a[i];for(int i=1; i<=n; i++){if(isa(a[i])) cnt++;//乱填的}cout << cnt;// cout << 5*18*5+45;// cout << endl << 27*18+9;
K、好奇的小夏
思路
本题有两种不太一样的解法,大概思想差不多。正解应该是前缀和+双指针或者是优先队列维护。但是我看有人O(n2)O(n^2)O(n2)遍历也过了,可能这题具体条件比较特殊。
解法1
用双指针进行滑动
- 先进行排序再求一下前缀和数组
- 左指针为此时刚好k值够用的地方,右指针为此时要变成的值
- 需要的操作次数是(r−l+1)∗a[r]−(sum[r]−sum[l−1])(r-l+1)*a[r]-(sum[r]-sum[l-1])(r−l+1)∗a[r]−(sum[r]−sum[l−1])
- 右指针先向右滑,直到k值不够用
- 此时左指针向右滑,直到k值刚好够用
- 每次到这个时候更新距离,注意:只有大于之前值的时候才更新数字,相等别更新!
解法2
用优先队列进行维护
-
先将整个序列的数进行压缩,压缩成{x,num}(值,数量)的形式存到结构体里
-
对结构体进行排序,保证结构体中的值是升序的
-
对结构体进行遍历:当前下标表示要变成这个数,优先队列里的元素是指目前k范围内可以变成的数
-
对于每个数,先假设将队列中所有的数都变成目前这个数
- 如果此时k没用完就不管,继续向后遍历
- 如果此时k为负值,先看看堆顶的元素全部剔除,直到此时全部剔除会使k>0
- 剩下的减去堆顶的一部分,直到k刚好不为0
-
对每一层处理完都判断是否要更新长度
代码
我是用的第二种,炒鸡难调😢
// Problem: 好奇的小夏
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/116435/K
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// Submission Time: 2025-08-27 13:46:02#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
#define fi first
#define se second
#define endl '\n'
const int INF = 1e18;
const int N = 1e6+5;
int a[N];
int n,m,k;
string s;
struct p
{int x;int num;
}f[N];
void solve()
{map<int,int> mp;cin >> n >> k;vector<int> v(n);for(int i=0; i<n; i++){cin >> v[i];mp[v[i]]++;//将序列压缩顺便排序}priority_queue<pii,vector<pii>,greater<pii>> q;int p=0;//记录结构体大小,即不同元素个数for(auto [x,num]: mp)//将map中排好序的元素放到结构体中,方便索引{p++;f[p].x=x;f[p].num=num;}//判断全部都一样的情况!!!!!!!!!!!!!!!!!!if(p==1)//此时就已经是最终状态,即所有数都一样{cout << f[1].num << ' ' << f[1].x;return ;}int mx=-INF,ans=-1;//0 2 3 4 6int cnt=f[1].num;//默认从第二层开始,此时堆里的数是第一层的q.push({f[1].x,f[1].num});for(int i=2; i<=p; i++)//开始一层层进行遍历{k-=(f[i].x-f[i-1].x)*cnt;//先默认将原来所有的数都变到当前层q.push({f[i].x,f[i].num});//将本层入堆while(!q.empty()&&q.top().se==0) q.pop();//如果堆顶被减完了就弹出if(q.empty()) continue;//避免空堆导致错误if(k<0)//此时操作次数不够用,需要将开始的弹出{while(!q.empty()&&(f[i].x-q.top().fi)*q.top().se<=abs(k))//即使将堆顶所有所有的元素都弹出都不够那么就整体弹出{cnt-=q.top().se;k+=(f[i].x-q.top().fi)*q.top().se;q.pop();if(k>=0) break;//直到此时大于0}if(k<0)//整体的弹出完了,将散的弹出{if(q.empty()) continue;int kk=abs(k)/(f[i].x-q.top().fi)+1;//计算弹多少cnt-=kk;int xx=q.top().fi;int pp=q.top().se-kk;q.pop();if(pp!=0)//如果堆顶就剩一个元素且弹出刚好够就不用入堆了q.push({xx,pp});k+=(f[i].x-xx)*kk;}}if(cnt+f[i].num>mx)//判断是否满足更新条件{mx=cnt+f[i].num;ans=f[i].x;}cnt+=f[i].num;//将本层元素入堆,更新堆内数量}cout << mx << ' ' << ans;
}
signed main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int T=1;// cin >> T;while(T--) solve();return 0;
}
L、打游戏 or 写程序
思路
本题有两种解法:动态规划、组合计数
动态规划
- 对于前k+1k+1k+1个事件,显然每个情况都是i+1i+1i+1种,既dp[i]=i+1dp[i]=i+1dp[i]=i+1dp[i]=i+1dp[i]=i+1dp[i]=i+1dp[i]=i+1dp[i]=i+1dp[i]=i+1
- 对于后面的每个事件分为两种情况:
- 如果选写程序那么答案累加dp[i−1]dp[i-1]dp[i−1]
- 如果选择打游戏显然答案累加dp[i−k−1]dp[ i- k- 1]dp[i−k−1]
- 综合起来转移就是dp[i]=dp[i−1]+dp[i−k−1]dp[i]=dp[i-1]+dp[i-k-1]dp[i]=dp[i−1]+dp[i−k−1],再对109+710^9+7109+7取模即可
代码
// Problem: 打游戏 or 写程序
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/116435/L
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)void solve()
{int n, k;cin >> n >> k;vector<int> f(n + 5);for (int i = 1; i <= k + 1; i ++ )f[i] = i + 1;for (int i = k + 2; i <= n; i ++ )f[i] = (f[i - 1] + f[i - k - 1]) % mod;cout << f[n] << endl;
}
组合计数
- 直接从0−n0-n0−n枚举写程序的个数,那么打游戏的数量就为n−in-in−i
- 又因为打游戏之间必须有k个写程序事件,所以用(n−i−(n-i-(n−i− 1)∗k1)*k1)∗k个写程序事件把打游戏隔开
- 此时写程序事件剩下i−(n−i−1)∗ki-(n-i-1)*ki−(n−i−1)∗k个,由于打游戏有n−in-in−i个事件,所以有了n−i+1n-i+1n−i+1个空格
- 用隔板法统计方案数为C(i−(n−i−1)∗k,n−i)C(i-(n-i-1)*k,n-i)C(i−(n−i−1)∗k,n−i),答案对109+710^9+7109+7取模即可。
代码
// Problem: 打游戏 or 写程序
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/116435/L
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)const int mod = 1e9 + 7;
int fac[N],infac[N];
int qmi(int a, int k)
{int ans = 1;while(k) {if (k & 1) ans = ans * a % mod;k >>= 1;a = a * a % mod;}return ans;
}void intal(int n)
{fac[0]=infac[0]=1;for(int i=1; i<=n; i++) {fac[i]=fac[i-1]*i%mod;infac[i]=infac[i-1]*qmi(i,mod-2)%mod;}
}int C(int a, int b)
{if(a<b||a<0||b<0) return 0;return fac[a]*infac[b]%mod*infac[a-b]%mod;
}void solve()
{int n, k;cin >> n >> k;intal(n+k);int ans=0;for (int i=0; i<=n; i++)ans=(ans+C(i-(n-i-1)*k+n-i,n-i))%mod;cout << ans << endl;}
M、奥数班
思路
签到题,纯模拟
代码
// Problem: 奥数班
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/116435/M
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// Submission Time: 2025-08-27 13:02:32void solve()
{cin >> n >> m;if(m*10<=n){cout << ">>";return ;}if(n>m) cout << ">";if(n==m) cout << "=";if(m>n){if(n*10<=m){cout << "<<";return ;}cout << "<";}
}