河南萌新联赛2025第(六)场:郑州大学
用起伏的背影挡住哭泣的心
有些故事不必说给每个人听
许多眼睛看的太浅太近
错过我没被看见那个自己
河南萌新联赛2025第(六)场:郑州大学
河南萌新联赛2025第(六)场:郑州大学_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ
第六场,只过了5题;这次比赛还是和之前的风格一样,有很多模板题;思维规律题收之前做过的题目的影响导致一直没写出来;一些算法题有学过,但是练得少就没有大胆去想,补题后发现也就是对模板的小变形;
没有发布官方的题解;按榜单来看题目难度大致为:L E I K J D F G H B C A
我的做题顺序为:L E I J D
目录
- 河南萌新联赛2025第(六)场:郑州大学
- L-整数商店的购物之旅
- E-数字支配
- I-外卖大战
- J-凹包
- D-穿过哈气之门
- K-还在分糖果!
- F-迷宫穿越
- G-宝石收集
- A-守护者之战
L-整数商店的购物之旅
哇,居然是二分?再一次败倒在二分;比赛是完全没有看出来而是去公式推导;
X=A×N+B×d(N)⇒X=A\times N+B\times d(N)\RightarrowX=A×N+B×d(N)⇒ X−B×d(N)=A×NX-B\times d(N)=A\times NX−B×d(N)=A×N
N=X−B×d(N)AN=\frac{X-B\times d(N)}{A}N=AX−B×d(N)
所以我们只需要枚举d(N)d(N)d(N)即可,题目中的范围最多18位;计算后判断n的位数是否与枚举的i相同即可;
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
#define lb(x) ((x)&(-x))
#define lc u<<1
#define rc u<<1|1
const int inf=0x3f3f3f3f;
const int N=1e5+5;
void slove(){int a,b,x;cin>>a>>b>>x;for(int i=18;i>0;i--){int n=x-b*i;if(n<0) continue;n/=a;if((int)log10(n)+1==i){if(n>1e9) n=1e9;cout<<n<<endl;return ;}}cout<<0;
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}
E-数字支配
小模拟,根据题意,如果子串全是9就直接输出,否则就输出长度减一个9;为什么用bool过不了qwq
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
#define lb(x) ((x)&(-x))
#define lc u<<1
#define rc u<<1|1
const int inf=0x3f3f3f3f;
const int N=1e5+5;
void slove(){string s;cin>>s;int ff=0;for(auto c:s){ff++;if(c!='9') break;}if(ff==s.size()||s.size()==1){cout<<s;return ;} for(int i=1;i<s.size();i++)cout<<9;
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}
I-外卖大战
小模拟,样例里面解释的很详细,根据题意模拟即可;细节比较多,狠狠吃罚时;
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
#define lb(x) ((x)&(-x))
#define lc u<<1
#define rc u<<1|1
const int inf=0x3f3f3f3f;
const int N=1e3+5;
int an[4],c[4],f[4];
void slove(){int n;cin>>n;for(int j=1;j<=n;j++){int x;cin>>x;for(int i=1;i<=3;i++){if(f[i]>=x){c[i]=0;f[i]++;an[i]++;for(int k=i+1;k<=3;k++){c[k]++;if(c[k]==3){f[k]+=2;c[k]=0;}}break;}c[i]++;if(c[i]==3){f[i]+=2;c[i]=0;}}}for(int i=1;i<=3;i++)cout<<an[i]<<' ';
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}
J-凹包
凸包模版
可以直接判凹包,但是比赛的时候交一直不对就直接建凸包了;最后只需要判断所有的点是否都在凸包上即可;如果凸包上的点的个数比题目中给的个数少就说明有点在凸包内,也就是凹多边形;
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
#define lb(x) ((x)&(-x))
#define lc u<<1
#define rc u<<1|1
const int inf=0x3f3f3f3f;
const int N=2e5+5;
pii a[N];
bool cmp(pii x,pii y){if(x.fi==y.fi) return x.se<y.se;return x.fi<y.fi;
}
int iaji(pii a,pii b,pii c){return (b.fi-a.fi)*(c.se-a.se)-(b.se-a.se)*(c.fi-a.fi);
}
pii tu[N];
int top=0;
void slove(){int n;cin>>n;for(int i=1;i<=n;i++)cin>>a[i].fi>>a[i].se;if(n==3){cout<<"No";return ;}sort(a+1,a+1+n);for(int i=1;i<=n;i++){while(top>1&&iaji(tu[top],tu[top-1],a[i])<0) top--;tu[++top]=a[i];}int t=top;for(int i=n-1;i>=1;i--){while(top>t&&iaji(tu[top],tu[top-1],a[i])<0) top--;tu[++top]=a[i];}if(top<=n) cout<<"Yes";else cout<<"No";
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}
D-穿过哈气之门
模拟,统计包含所有种类的区间的个数;利用双指针滑动,在满足的区间进行一次统计;在最后一次循环的时候会多算一次,所以结果减1一下即可;
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
#define lb(x) ((x)&(-x))
#define lc u<<1
#define rc u<<1|1
const int inf=0x3f3f3f3f;
const int N=1e5+5;
int a[N];
map<int,int> mp;
void slove(){int n,m;cin>>n>>m;for(int i=1;i<=n;i++)cin>>a[i];int l=1,r=0;int c=0;int an=0;while(r<n){while(r<n){r++;if(mp[a[r]]==0)c++;mp[a[r]]++;if(c==m)break;}an+=(n-r+1);while(l<r){mp[a[l]]--;if(mp[a[l]]==0) c--;l++;if(c!=m)break;an+=(n-r+1);}}cout<<an-1;
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}
K-还在分糖果!
比赛的时候一直被之前做过的一个题目误导,所以一直在考虑递推;因为7会被拿走,所以可以看做是9进制;将n转换成9进制数即可,如果当前位的数大于7就加一,将7跳过即可;
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
#define lb(x) ((x)&(-x))
#define lc u<<1
#define rc u<<1|1
const int inf=0x3f3f3f3f;
const int N=1e5+5;
void slove(){int n;cin>>n;string an;while(n){int c=n%9;if(c>=7) c++;an+=('0'+c);n/=9;}reverse(an.begin(),an.end());cout<<an<<endl;
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;cin>>_;while(_--)slove();return 0;
}
F-迷宫穿越
最短路搜索,这题没过太不应该了!之前也遇到好多次了,赛时一直调不出bug,一直超时;
其实普通的bfs就能过,开个三维标记一下访问就可以了;
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
#define lb(x) ((x)&(-x))
#define lc u<<1
#define rc u<<1|1
const int inf=0x3f3f3f3f;
const int N=1e3+5;
int dx[]={1,0,-1,0,1,-1,1,-1};
int dy[]={0,1,0,-1,1,-1,-1,1};
int n,m,k,q,x,y,ans;
struct node{int x,y,d,t;
};
char g[N][N];
int vis[N][N][15];
void slove(){cin>>n>>m>> k;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>g[i][j];vis[1][1][0]=1;queue<node> q;q.push({1,1,0,0});while(q.size()){auto [x,y,d,t]=q.front();q.pop();if(x==n&&y==m){cout<<d<<endl;return;}for(int i=0;i<4;i++){int nx=x+dx[i],ny=y+dy[i];if(nx<1||ny<1||nx>n||ny>m) continue;if(g[nx][ny]=='#'){if(t==k||vis[nx][ny][t+1]) continue;q.push({nx,ny,d+1,t+1});vis[nx][ny][t+1]=1;}else{if(vis[nx][ny][t]) continue;q.push({nx,ny,d+1,t});vis[nx][ny][t]=1;}}}cout<<-1<<endl;
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}
G-宝石收集
状态压缩dp!n只有20,考虑状态压缩!之前虽然学习过状态压缩,但是没有遇到对应的题目就没有看出来;
每个宝石的编号为0−n−10-n-10−n−1,由于nnn并不大,采用状态压缩存储每个地下城拥有的宝石种类,采用二进制存储,如果二进制数的第iii位为1,代表该地下城拥有编号为iii的宝石。
那么对于我们拥有的宝石状态为t,我们每进入一个地下城,只可能获得宝石,并不会去失宝石,只会让t变大而不会变小,只会向更大的状态转移,不会影响小状态,满足无后效性,考虑动态规划。令dpidp_idpi表示我们拥有的宝石的状态为iii时的最小花费。
假设标号为jjj的地下城拥有的宝石的状态为aja_jaj,那么状态转移方程为:dpi∣aj=min(dpi∣ai,dpi+vj)dp_i|a_j=min(dp_{i|a_i},dp_i+v_j)dpi∣aj=min(dpi∣ai,dpi+vj);
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
#define lb(x) ((x)&(-x))
#define lc u<<1
#define rc u<<1|1
const int inf=0x3f3f3f3f;
const int N=1e2+5;
int dp[(1ll<<21)];
pii a[N];
void slove(){int n,m;cin>>n>>m;memset(dp,inf,sizeof dp);dp[0]=0;for(int i=0;i<m;i++){int x;cin>>a[i].se>>x;int t=0;for(int i=0;i<x;i++){int y;cin>>y;t+=1<<y;}a[i].fi=t;}for(int i=0;i<(1ll<<n);i++){for(int j=0;j<m;j++){int t=i|a[j].fi;dp[t]=min(dp[t],dp[i]+a[j].se);}}if(dp[(1<<n)-1]==inf) cout<<-1<<endl;else cout<<dp[(1<<n)-1]<<endl;
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}
A-守护者之战
这题的题目描述不是很好,而且数据也比较水,只需要判断我方有无血量为1的人即可;可以看看上一年的原题;这里面描述的比较清晰;
这里给出正确的思路;
题面较抽象,简单来说就是,你有n个侍从,m个敌人,你的每个侍从最多攻击一次,如果该侍从的血量不为 0,它就可以释放攻击,会使得自己和被攻击者同时扣除一点血量,如果侍从或者敌人血量归 0,就会对场上所有单位进行打击,包括队友,全部扣除一点血量,请问你是否能通过某种攻击顺序击败所有敌人。
这题采取贪心的,关键在于如何高效地安排两种不同类型的操作(处理数组 a和数组 b)以满足特定条件。
考虑贪心,不难发现,我们应该最大化自己的攻击次数,对于血量为1的侍从,一旦它攻击,自己就会死,并且让其他血量同为1的侍从一起死,总共只能攻击一次,并且不能太早进行攻击,否则会影响到其他血量稍高的侍从后续的攻击;为了尽量攻击更多次,我们假设存在一种贪心方案,可以让所有血量非1的侍从都能攻击一次,验证该方案是否存在,血量大于 1 的侍从只要攻击血量非 1的敌人,就不会引发爆炸,如果不存在这种选择,说明所有敌人血量都是 1了,此时我们选择就会炸死所有敌人,已经结束了,否则我们就能一直选下去,保证最大化攻击次数。
于是考虑双指针,由于我们假定了每个人都能攻击到一次,那么实际上每个侍从的实际血量就是ai−1a_i−1ai−1,记录当前爆炸的造成的伤害,初始为 0,如果血量最小的侍从或者血量最少的敌人血量低于爆炸伤害,就让爆炸伤害加 1,并且让指针右移,否则的话就让血量最少的侍从攻击血量最少的敌人,试图引爆对方来造成爆炸,重复上述步骤即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
#define lb(x) ((x)&(-x))
#define lc u<<1
#define rc u<<1|1
const int inf=0x3f3f3f3f;
const int N=5e5+5;
int a[N],b[N];
void slove(){int n,m;cin>>n>>m;int c=0;for(int i=1;i<=n;i++){cin>>a[i];if(a[i]>1) c++;}sort(a+1,a+1+n);if(a[1]==1) c++;for(int i=1;i<=m;i++)cin>>b[i];sort(b+1,b+1+m);int la=0,lb=0,p=0;while(lb<m){while(la<n&&a[la+1]-1<=p){p++;la++;}if(b[lb+1]>p){c-=(b[lb+1]-p);if(c<0) break;}lb++;p++;}if(lb==m) cout<<"YES"<<endl;else cout<<"NO"<<endl;
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;cin>>_;while(_--)slove();return 0;
}