集训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\)

\[y^{2^n}\le x\le (y+1)^{2^n} \]

取对数后,不等式转化为

\[2^n(\log_2(y+1)-\log_2{y})>1 \]

所以一定存在正整数 \(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;
posted @   才瓯  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示