河南萌新联赛2026第(四)场:南阳理工学院
补题入口直接点题目即可
难度:
签到:M , G
easy:L , J
mid:D , H , I
mid - hard:C , B , E , F
hard:K , A
M-噢耶,奶奶的龙!!!
高数题,有界乘无穷小算出来极限是0,输出就好了
G-你逃不过我你信不信
题目:
输入一个正整数n,求1到n的阶乘和的最后四位数字(不够4位的向前补0)
思路:
阶乘和,如果阶乘里有10000之后那往后再怎么求和也不会改变最后四位的数值了,所以你可以想一下,2,5,10,15,20,这里的5因子已经有四个,分别都乘2,那就是四个0了,所以在阶乘20之后的数后面四位就不会改变了
代码:
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
void solve()
{
int n; cin>>n;
if(n>=20){
cout<<"0313"<<endl;
return;
}
int m=1,num=0;
for(int i=1;i<=n;i++){
m*=i;
num+=m;
}
int p=num%10000;
int pp=p;
int k=0;
while(pp>0){
pp/=10;
k++;
}
while(1){
if(k<4){
cout<<0;
k++;
}else{
break;
}
}
cout<<p;
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
//cin>>_;
while(_--)
solve();
return 0;
}
好像ic省赛有这道题
L-A × B
高精度乘法的模板题,赛时忘记咋写了被自己气哭了
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
const int INF=1e6+5;
void solve()
{
string s1,s2;
cin>>s1>>s2;
int l1=s1.size();
int l2=s2.size();
reverse(s1.begin(),s1.end());
reverse(s2.begin(),s2.end());
vector<int> a(l1+l2,0);
for(int i=0;i<l1;i++){
for(int j=0;j<l2;j++){
a[j+i]+=(s1[i]-'0')*(s2[j]-'0');
}
}
for(int i=0;i<l1+l2-1;i++){
a[i+1]+=a[i]/10;
a[i]=a[i]%10;
}
while(a.size()>1&&a.back()==0) a.pop_back();
for(int i=a.size()-1;i>=0;i--){
cout<<a[i];
}
// cout<<fixed<<setprecision(x)<<
}
signed main()
{
IOS;
int _=1;
// cin>>_;
while(_--)
solve();
return 0;
}
J-小苯的星轨
题目:
平面直角坐标系中有 n 颗星星,第 i 颗星星位于整点(xi,yi)
保证所有星星的位置两两不同,但允许某颗星星位于原点 (0,0)
小苯会选择两颗不同的星星,组成一个无序星星对。如果经过这两颗星星的直线同时经过原点 (0,0)那么称这个星星对是合法的
1、 选择第 i 颗和第 j 颗星星,与选择第 j 颗和第 i 颗星星是同一个星星对
2、 如果两颗星星中有一颗位于原点,那么这个星星对一定合法
3、 由于所有星星的位置两两不同,任意两颗星星都能确定唯一一条直线
请计算合法无序星星对的数量
思路:
原点是比较特殊的,所以要单独处理,两个点想要共线就要斜率相同,所以我们用map把斜率都存起来就可以知道多少点可以构成这个斜率的直线,在算斜率时要化成最简方向向量,求最大公约数去化简,然后统一符号,确保方向相反的两个向量被映射到同一个键,然后C(cnt, 2) = cnt * (cnt - 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;
// int gcd(int a,int b){
// a=abs(a),b=abs(b);
// while(b){
// int t=a%b;
// a=b;
// b=t;
// }
// return a;
// }
void solve(){
int n;
cin>>n;
map<pii,int>mp;
int a=0;
int num=0;
for(int i=1;i<=n;i++){
int x,y; cin>>x>>y;
if(x==0&&y==0){
num+=n-1;
}else{
int g=__gcd(x,y);
x=x/g;
y=y/g;
mp[{x,y}]++;
}
}
for(auto &pr:mp){
int cnt=pr.se;
num+=cnt*(cnt-1)/2;
}
cout<<num<<endl;
}
signed main(){
IOS;
int _=1;
cin>>_;
while(_--) solve();
return 0;
}
D-小圆爱玩龙龙(Easy)
题目:
在N件物品中选择一些购买,每个道具有价格wi,和快乐值vi 允许免费拿走一个,其他道具最多只能拿一次,且总花费不能超过W,最多可以获得多少快乐值
思路:
只能拿一次,就是01背包问题,但是多了一个条件可以免费拿一个,那就枚举免费拿每一件物品,其他的物品用01背包求出W最大能多少快乐值
代码:
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, W;
cin >> n >> W;
vector<int> w(n), v(n);
//读入每件物品重量w[i],价值v[i]
for (int i = 0; i < n; i++) {
cin >> w[i] >> v[i];
}
int ans = 0;
// f:枚举哪一件物品作为【免费拿走】的物品
// f从0到n‑1,依次假设第f件免费
for (int f = 0; f < n; f++) {
vector<int> dp(W + 1, 0); // dp[j]:花费j元能拿到的最大快乐值
//遍历所有物品
for (int i = 0; i < n; i++) {
if (i == f) continue; //免费的这件跳过,不参与背包(不用花钱买)
//标准01背包倒序循环
for (int j = W; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
// dp[W]:剩下物品用最多W元得到的最大价值;再加上免费物品的价值v[f]
ans = max(ans, dp[W] + v[f]);
}
cout << ans << '\n';
return 0;
}
H-小苯的镜像符文
题目:
一个空的串,要求绘制一个长度为n的字符串s,可以执行两种操作
- 手动绘制,在字符串末尾绘制一个小写英文字母,手动绘制分别有能量消耗
- 镜像法术,选择当前字符串的一个非空后缀,将它翻转追加到字符串末尾,一次消耗k点能量
不能删除或修改已经绘制好的字符,求出绘制出s所需的最少能量
思路:
操作二其实就是找回文字符串,如果操作2的k点能量比操作1手动绘制要小,那自然要用操作二,所以操作二就是在字符串s中找回文字符串,每次从第i个字符开始扩展L- -,R++只要sL==sR,就可以直接在位置i跳到位置R,耗费k点能量,
然后用dp一个数组记录每个前缀的最小代价,然后枚举所有可能的转移,取最小值
这里为什么用dp讲一下自己的理解吧,首先还是因为这题要考虑全局的情况,就是一次镜像虽然跨度大但是能量消耗也可能大,手绘的话可能跨度小,但是累计起来可能更便宜所以需要全局衡量不能贪心,然后每个位置耗费能量的最小值一定是构造前面字符的最优解,想得到现在的最优状态肯定是由小的状态组成的,就是要保证最优子结构,然后题目中给了两种状态转移,同时从左往后绘制的过程和状态的计算同步,一定是之前的状态推出之后的状态,满足状态的一种"单调性"
所以用dp去写,第一个字符只能手绘,后续看字符串s判断是不是回文串,再用两种操作所需要的能量去更新当前状态的最小值,然后每次都选择最小值单向递推最终得到答案
代码:
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin >> T;
while (T--) {
int n;
int k;
cin >> n >> k;
string s;
cin >> s;
vector<int> cost(26);
for (int i = 0; i < 26; ++i) cin >> cost[i];
const int INF = (1LL << 62);
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] << '\n';
}
return 0;
}
I-小苯的括号高度
题目:

思路:
题目很简单,最大高度就是n2 最小呢就是n 所以在这范围之外的直接输出-1 即可
为什么呢:
以
n = 3,串((()))为例高度序列为:
[1, 2, 3, 2, 1, 0]高度和 = 1 + 2 + 3 + 2 + 1 + 0 = 9
这不久看出来了,除了最高的两两配对就刚刚好是n2
任何合法括号串的高度和与 n 的差恒为偶数,所以 k-n 必须是偶数
贪心构造:
先令目标值减去最小高度和,再除以 2,得到还需要增加的贡献 ,当处理第 m 对括号时,如果选择"包在外面",就会额外贡献 m-1(内部每一个高度都加1),我们依次对于每一对括号,都有两种选择吗,直接放在末尾额外贡献0,包在外面额外贡献m-1,所以问题变成:从 0,1,2,...,n−1中选一些数,使它们的和等于目标贡献由于这些数可以表示从 0 到最大值之间的所有整数,因此从大到小贪心选择即可
代码:
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
long long n, k;
cin >> n >> k;
if (k < n || k > n * n || (k - n) % 2) {
cout << -1 << '\n';
continue;
}
long long need = (k - n) / 2;//算出需要增加的贡献
vector<int> wrap(n + 1);
for (int m = n; m >= 1; m--) {
if (m - 1 <= need) {//从大到小贪心,最终一定能凑出need
wrap[m] = 1;
need -= m - 1;
}
}
deque<char> dq;
for (int m = 1; m <= n; m++) {
if (wrap[m]) {
dq.push_front('(');
dq.push_back(')');
} else {
dq.push_back('(');
dq.push_back(')');
}
}
for (char c : dq) cout << c;
cout << '\n';
}
return 0;
}