集训5 20250209
集训5 20250209
牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ
A:
题目大意:给出一个整数和一个操作符,生成两个能组成这个数的因子
#include<bits/stdc++.h> #define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; int main() { long long x; char op; cin>>x>>op; if (op=='*'){ cout<<x<<' '<<1<<endl; } if (op=='+'){ cout<<x-1<<' '<<1<<endl; } if (op=='-'){ cout<<x+1<<' '<<1<<endl; } return 0; }
签到模拟
J:
题目大意:给出一个字符串,根据字符的不同计算总路程,不同字符表示对速度的不同操作
#include<bits/stdc++.h> #define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; int main() { int n; cin>>n; long long ans=0,v=0; vector<char> op(n+5); for (int i=1;i<=n;i++) cin>>op[i]; for (int i=1;i<=n;i++){ if (op[i]=='0'){ v+=10; ans+=v; } if (op[i]=='1'){ if (v-5>=0){ v-=5; ans+=v; } else{ v=0; ans+=v; } } if (op[i]=='2'){ if (v-10>0){ v-=10; ans+=v; v+=10; }else{ int t=v; v=0; ans+=v; v=t; } } } cout<<ans<<endl; return 0; }
v
记录当前的速度,ans
存答案,乱模拟就行
B:
题目大意:有 \(n\) 个元素,其中存在 \(k\) 个元素被标记,\(t\) 个以上没有被标记的连续元素为一组,排列被标记的数后求分组的最大数量
#include<bits/stdc++.h> #define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; long long n,t,k; bool judge(long long x){ if (t*x+k<=n) return 1; else return 0; } void solve(void){ cin>>n>>t>>k; long long lb=(t+1)*k; if (lb<=n){ if (n-lb>=t) cout<<k+1<<endl; else cout<<k<<endl; } else{ long long l=-1,r=k+1; while (l+1!=r){ long long mid=l+r>>1; if (judge(mid)) l=mid; else r=mid; } cout<<l<<endl; } } int main() { int T; cin>>T; while(T--) solve(); return 0; }
原本想得太复杂了卡了 \(4\) 发,最终的代码虽然正确但是不够简单,用了二分来算
实际上仔细审视这个问题,将 \(k\) 个被标记的元素去除后我们还能用 \(n-k\) 个元素,那么这些元素可以分出 \(\lfloor\frac{n-k}{t}\rfloor\) 组
但是这 \(k\) 个被标记的元素最多能将这 \(n\) 个元素分为 \(k+1\) 组
所以只需要简单的判断最小值即可
void solve() { int n, t, k; cin >> n >> t >> k; cout << min(k+1,(n-k)/t)<<endl; }
I:
题目大意:给出两个数 \(n,m\) 可以进行两种操作
- 将 \(n\) 乘以二
- 将 \(n\) 开方后向下取整
判断是否能通过以上两种操作使 \(n\) 变为 \(m\)
#include<bits/stdc++.h> #define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; void solve(void){ int n,m; cin>>n>>m; if (n==0&&m!=0||m==0&&n!=0){ cout<<"NO"<<endl; return; } cout<<"YES"<<endl; return; } int main() { int T; cin>>T; while (T--) solve(); return 0; }
猜的结论,当且仅当 \(n,m\) 中仅有一个为 \(0\) 时才不能转变,否则一定能组合出答案
证明如下:
存在一个数 \(x\)
取对数后,不等式转化为
所以一定存在正整数 \(m\) 使得 \(x=2^m\) ,\(x\) 经过多次操作可以得到 \(y\)
C:
题目大意:
#include<bits/stdc++.h> #define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; long long n,x,y; int main() { cin>>n>>x>>y; string a,b,c; cin>>a>>b>>c; int cnt[4]={0}; for (int i=0;i<n;i++){ if (((a[i]-'0')^(b[i]-'0'))!=(c[i]-'0')){ if (a[i]=='0'&&b[i]=='0') cnt[0]++; if (a[i]=='0'&&b[i]=='1') cnt[1]++; if (a[i]=='1'&&b[i]=='0') cnt[2]++; if (a[i]=='1'&&b[i]=='1') cnt[3]++; } } long long ans=1ll*x*(cnt[0]+cnt[1]+cnt[2]+cnt[3]); sort(cnt,cnt+4); if (cnt[3]>cnt[0]+cnt[1]+cnt[2]){ ans=min(ans,(cnt[0]+cnt[1]+cnt[2])*y+(cnt[3]-cnt[0]-cnt[1]-cnt[2])*x); }else{ ans=min(ans,(cnt[0]+cnt[1]+cnt[2]+cnt[3])/2*y+(cnt[0]+cnt[1]+cnt[2]+cnt[3])%2*x); } cout<<ans<<endl; return 0; }
核心想法:分别记录 a^b!=c
的四种情况的种数
if (a[i]=='0'&&b[i]=='0') cnt[0]++; if (a[i]=='0'&&b[i]=='1') cnt[1]++; if (a[i]=='1'&&b[i]=='0') cnt[2]++; if (a[i]=='1'&&b[i]=='1') cnt[3]++;
\(0\oplus0=1\oplus1=0,0\oplus1=1\oplus0=1\),那么这四种情况有一个潜在的性质
假设 \(a\oplus b=1\) ,将 \(a,b\) 中任一元素取反后,\(a\oplus !b=!a\oplus b=!1=0\)
特别的对于两个不同的 a,b
元素组,如果它们都不匹配 c
,我们一定可以交换其中某一位 \(0,1\) 来使得这两个都能匹配 c
首先计算如果全部都采用替换的办法,那么需要的代价总值为 x*(cnt[0]+cnt[1]+cnt[2]+cnt[3])
然后考虑可以交换的情况:对这四种情况按数量进行排序
- 如果一种情况比其余情况都要多,那么只用考虑其余情况和最多的情况进行交换操作,剩下的就进行替换
- 如果如果一种情况比其余情况要少,进行交换后要么刚好可以两两抵消,要么剩下一组,这一组单独进行替换即可(组数为奇数)
if (cnt[3]>cnt[0]+cnt[1]+cnt[2]){//cnt[3]>(cnt[0]+cnt[1]+cnt[2]+cnt[3])/2,大于总数的一半 ans=min(ans,(cnt[0]+cnt[1]+cnt[2])*y+(cnt[3]-cnt[0]-cnt[1]-cnt[2])*x); }else{ ans=min(ans,(cnt[0]+cnt[1]+cnt[2]+cnt[3])/2*y+(cnt[0]+cnt[1]+cnt[2]+cnt[3])%2*x); }
E:
题目大意:
#include<bits/stdc++.h> #define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; char mtx[5][5]; bool check(void){ for (int i=1;i<=3;i++){ bool f=0; int cnt=0; for (int j=1;j<=3;j++){ if (mtx[i][j]=='X') f=1; if (mtx[i][j]=='X'||mtx[i][j]=='G') cnt++; } if (cnt==3&&f) return 1; } for (int i=1;i<=3;i++){ bool f=0; int cnt=0; for (int j=1;j<=3;j++){ if (mtx[j][i]=='X') f=1; if (mtx[j][i]=='X'||mtx[j][i]=='G') cnt++; } if (cnt==3&&f) return 1; } int cnt=0; bool f=0; for (int i=1;i<=3;i++){ if (mtx[i][i]=='X') f=1; if (mtx[i][i]=='X'||mtx[i][i]=='G') cnt++; } if (cnt==3&&f) return 1; cnt=0; f=0; for (int i=1;i<=3;i++){ if (mtx[i][4-i]=='X') f=1; if (mtx[i][4-i]=='X'||mtx[i][4-i]=='G') cnt++; } if (cnt==3&&f) return 1; return 0; } void solve(void){ for (int i=1;i<=3;i++) for (int j=1;j<=3;j++) cin>>mtx[i][j]; int cnt=0; for (int i=1;i<=3;i++){ for (int j=1;j<=3;j++){ if (mtx[i][j]=='X') cnt++; } } if (cnt<=2){ cout<<"Yes"<<endl; return; } if (check()) cout<<"Yes"<<endl; else cout<<"No"<<endl; } int main() { int T; cin>>T; while (T--) solve(); return 0; }
一开始想得太复杂了,DFS打表乱写一通
推理可知:当每个人都下了 \(0,1,2\) 步时,先手的人一定会赢,剩下的情况分类讨论即可
-
双方各下了 \(3\) 步时,此时场上还剩 \(3\) 个空位,先手的人可以一次性下两步,那么将这个棋盘扫一遍
判断每行每列,以及两个对角线是否满足下两步后连成三个即可
-
双方各下了 \(4\) 步时,同样地扫描这个棋盘,判断即可
当且仅当行,列,对角线上以及下了1个或2个X后,才能赢 OXX GOX XOO XGO GGG OXG //都是输 for (int i=1;i<=3;i++){//扫每行 bool f=0;//标记是否这一行存在下过的棋子 int cnt=0; for (int j=1;j<=3;j++){ if (mtx[i][j]=='X') f=1; if (mtx[i][j]=='X'||mtx[i][j]=='G') cnt++; } if (cnt==3&&f) return 1; }
扫描对角线同理
int cnt=0; bool f=0; for (int i=1;i<=3;i++){ if (mtx[i][i]=='X') f=1; if (mtx[i][i]=='X'||mtx[i][i]=='G') cnt++; } if (cnt==3&&f) return 1;
L:
题目大意:从 \([1,n]\) 中选出一个三元组 \((x,y,z)\) 组成的三对数中恰好有两对数互质
#include<bits/stdc++.h> #define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; void solve(void){ long long n; cin>>n; if (n%6<3){ cout<<n/6*2<<endl; long long i=1; for (i=1;i<=n/6*6;i+=6){ cout<<i<<' '<<i+1<<' '<<i+3<<endl; cout<<i+2<<' '<<i+4<<' '<<i+5<<endl; } }else{ if (n<=3){ cout<<0<<endl; return; } cout<<n/6*2+1<<endl; long long i=1; for (i=1;i<=n/6*6-6;i+=6){ cout<<i<<' '<<i+1<<' '<<i+3<<endl; cout<<i+2<<' '<<i+4<<' '<<i+5<<endl; } if (n>=4) cout<<i<<' '<<i+1<<' '<<i+3<<endl; if (n>=8) cout<<i+5<<' '<<i+6<<' '<<i+7<<endl; if (n>=9) cout<<i+2<<' '<<i+4<<' '<<i+8<<endl; } } int main(){ int T; cin>>T; while (T--) solve(); return 0; }
找规律,结论题
考虑橙蓝列都互质,一般情况的三元组 \((x,y,z)\) 可以为 \((i,i+1,i+3),(i+2,i+4,i+5)\),以 \(6\) 为一周期
当给出的 \(n\) 不同时,分为两种情况
- \(n\ \%\ 6<3\) 时,多出来的元素只有两个,无法构成更多的三元组
特判 \(n\le 3\) 时无答案
if (n<=3){ cout<<0<<endl; return; }
-
\(n\ \%\ 6\ge3\) 时,多出来的元素又可以构造一个三元组
与最后一个周期的六个元素一起,杂交构造 \(3\) 个,\((i,i+1,i+3),(i+2,i+4,i+8),(i+5,i+6,i+7)\)
- 同时当 \(n\le9\) 时需要特判,因为没有最后一个周期
if (n>=4) cout<<i<<' '<<i+1<<' '<<i+3<<endl; if (n>=8) cout<<i+5<<' '<<i+6<<' '<<i+7<<endl; if (n>=9) cout<<i+2<<' '<<i+4<<' '<<i+8<<endl;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具