当前位置: 首页 > news >正文

牛客周赛Round145

这次比较有进步,可能是因为题不是很难吧,A了5个

目录

A题:小红的好串

map

B题:小红的好串计数

双指针

C题:小红的染色平均数

分数计算

核心目标

关键发现

数学推导

最少操作次数计算

D题:小红的排列构造

构造

核心目标

关键发现

核心逻辑

E题:小红的染色生成树

最小生成树+强制加边

核心目标

关键发现

核心逻辑


A题:小红的好串

map

这个主要就是用map统计一下字符出现个数就好了嘛,不用多说

#include<bits/stdc++.h> using namespace std; string s; int main() { cin>>s; unordered_map<char,int>mp; for(int i=0;i<s.size();i++) { mp[s[i]]++; } bool flag=false; for(int i=0;i<s.size();i++) { if(mp[s[i]]==2) { flag=true; break; } } if(flag)cout<<"Yes"<<endl; else cout<<"No"<<endl; return 0; }

B题:小红的好串计数

双指针

先算出所有子串的个数,然后再减去不是好串的子串个数就好了,不是子串,就是连续字符相同嘛,就是双指针

#include<bits/stdc++.h> using namespace std; const int N=1e6+10; typedef long long LL; string s; int main() { int n; cin>>n>>s; LL res=1LL*n*(n+1)/2; for(int i=0;i<n;) { int j=i; while(s[i]==s[j]&&j<n)j++; int num=j-i; res-=1LL*num*(num+1)/2; i=j; } cout<<res<<endl; return 0; }

C题:小红的染色平均数

分数计算

核心目标

让红色元素的平均值 = 蓝色元素的平均值。

关键发现

  1. 操作不改变总和:每次 “一加一减”,整个数组的总和不变。
  2. 只跨颜色操作才有效:同颜色之间的加减,对平均值没任何影响。

数学推导

设:

  • cnt0:红色元素个数,sum0:红色元素总和
  • cnt1:蓝色元素个数,sum1:蓝色元素总和

要让平均值相等,等价于:sum0 / cnt0 = sum1 / cnt1交叉相乘消分母:sum0 * cnt1 = sum1 * cnt0

最少操作次数计算

每次跨颜色操作,会让上面的差值变化n(总元素个数)。所以最少操作次数就是:|sum0 * cnt1 - sum1 * cnt0| / n。如果差值不能被n整除,就永远不可能实现,输出-1

#include<bits/stdc++.h> using namespace std; const int N=2e5+10; int a[N]; string s; typedef long long LL; int n; int main() { cin>>n; for(int i=0;i<n;i++)cin>>a[i]; cin>>s; LL sum1=0,sum0=0; LL res1=0,res0=0; for(int i=0;i<n;i++) { if(s[i]=='1') { sum1++; res1+=a[i]; } else { sum0++; res0+=a[i]; } } //cout<<sum1<<' '<<sum0<<endl; //cout<<res1<<' '<<res0<<endl; LL num=sum1+sum0; res1*=sum0; res0*=sum1; //cout<<res1<<' '<<res0<<endl; if(abs(res1-res0)%num) { cout<<-1<<endl; } else { cout<<abs(res1-res0)/num<<endl; } return 0; }

D题:小红的排列构造

构造

核心目标

构造两个长度为 n 的排列 b 和 c,满足:

  1. 每个位置 i,b [i] 或 c [i] 等于 a [i]
  2. b 和 c 中等于 a [i] 的位置都至少有 n/2 个
  3. b 和 c 都是 1~n 的排列(每个数恰好出现一次)

关键发现

  1. 最多出现 2 次:任何数在 a 中出现超过 2 次就一定无解,因为两个排列加起来最多只能放 2 个这个数。
  2. 单次数双份放:只出现 1 次的数,可以同时放在 b 和 c 的对应位置,既不破坏排列(因为只出现一次),又能同时增加两个数组的匹配数。
  3. 空位数量相等:出现 2 次的数会在每个数组里各留下 k 个空位,而 a 中没出现过的数也正好有 k 个,刚好能填满。

核心逻辑

  1. 先判无解:统计每个数的出现次数,只要有一个数出现超过 2 次,直接输出 - 1。
  2. 双次数分家:对于出现 2 次的数,第一次出现放在 b 数组,第二次出现放在 c 数组。这样既保证了每个位置都有一个数组等于 a [i],又保证了两个数组里这个数都只出现一次。
  3. 单次数双放:对于出现 1 次的数,b 和 c 数组在这个位置都直接放 a [i]。这一步是整个算法的灵魂,它让两个数组的匹配数自动都达到了 n-k(k 是出现 2 次的数的个数),而 k≤n/2,所以自动满足 "至少 n/2 个匹配" 的要求。
  4. 循环填空位:把 a 中没出现过的数收集起来,循环填充到 b 和 c 数组剩下的空位里,保证两个数组都是合法的排列。
#include<bits/stdc++.h> using namespace std; const int N=2e5+10; int a[N]; int x[N],y[N]; int cnt[N]; bool st[N]; int main() { int n; cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++) { if(a[i]>n) { cout<<-1<<endl; return 0; } cnt[a[i]]++; if(cnt[a[i]]>2) { cout<<-1<<endl; return 0; } } queue<int>q; for(int i=1;i<=n;i++) { if(cnt[i]==0)q.push(i); } for(int i=1;i<=n;i++) { if(cnt[a[i]]==2) { if(!st[a[i]]) { x[i]=a[i]; st[a[i]]=true; } else y[i]=a[i]; } } for(int i=1;i<=n;i++) { if(cnt[a[i]]==2) { if(x[i])cout<<x[i]<<' '; else { cout<<q.front()<<' '; q.push(q.front()); q.pop(); } } else cout<<a[i]<<' '; } cout<<endl; for(int i=1;i<=n;i++) { if(cnt[a[i]]==2) { if(y[i])cout<<y[i]<<' '; else { cout<<q.front()<<' '; q.push(q.front()); q.pop(); } } else cout<<a[i]<<' '; } return 0; }

E题:小红的染色生成树

最小生成树+强制加边

核心目标

找一棵原图的生成树,满足:

  1. 恰好包含两种颜色的边(不能是单色,也不能是三色)
  2. 是合法的生成树(n-1 条边,连通所有节点,无环)

关键发现

  1. 颜色只有三种:所以可能的双色组合一共只有 3 种,暴力枚举完全可行,时间复杂度毫无压力
  2. 直接跑 Kruskal 会踩坑:如果其中一种颜色的边本身就能连通整个图,普通 Kruskal 会生成一棵单色树,不符合题目要求
  3. 强制加边就能解决问题:只要在跑 Kruskal 之前,先手动加入一条第一种颜色和一条第二种颜色的边,就能 100% 保证生成树是双色的

核心逻辑

  1. 枚举所有组合:依次检查 (0,1)、(0,2)、(1,2) 这三种双色组合
  2. 第一步:可行性检查
    • 检查图中是否同时存在这两种颜色的边,如果缺一种,直接跳过这个组合
    • 检查只用这两种颜色的边,能不能把整个图连通,如果不能,也跳过
  3. 第二步:强制加边(最关键的一步)
    • 随便找一条第一种颜色的边,加入生成树,合并它的两个端点
    • 再随便找一条第二种颜色的边,加入生成树,合并它的两个端点
    • 这一步直接解决了 "生成单色树" 的大坑,保证了最终的树一定有两种颜色
  4. 第三步:补全剩余边
    • 用标准的 Kruskal 算法,遍历所有边
    • 只要是这两种颜色的边,并且不会形成环,就加入生成树
    • 直到生成树有 n-1 条边为止
  5. 输出结果:只要找到一个合法的生成树,就直接输出并结束程序
#include<bits/stdc++.h> using namespace std; const int N=100010,M=200010; int p[N]; int n,m; struct Edge { int a,b,w; }edges[M]; int find(int u) { if(p[u]!=u) p[u]=find(p[u]); return p[u]; } bool check(int c1,int c2) { for(int i=1;i<=n;i++) p[i]=i; for(int i=0;i<m;i++) { int a=edges[i].a,b=edges[i].b,w=edges[i].w; if(w==c1 || w==c2) { a=find(a),b=find(b); if(a!=b) p[a]=b; } } int root=find(1); for(int i=2;i<=n;i++) if(find(i)!=root) return false; return true; } int find_edge(int color) { for(int i=0;i<m;i++) if(edges[i].w==color) return i; return -1; } int main() { cin>>n>>m; for(int i=0;i<m;i++) { int a,b,w; cin>>a>>b>>w; edges[i]={a,b,w}; } int pairs[3][2]={{0,1},{0,2},{1,2}}; for(int k=0;k<3;k++) { int c1=pairs[k][0],c2=pairs[k][1]; bool has1=false,has2=false; for(int i=0;i<m;i++) { if(edges[i].w==c1) has1=true; if(edges[i].w==c2) has2=true; } if(!has1 || !has2) continue; if(!check(c1,c2)) continue; int res[N]; int cnt=0; for(int i=1;i<=n;i++) p[i]=i; int e1=find_edge(c1); res[cnt++]=e1; p[find(edges[e1].a)]=find(edges[e1].b); int e2=find_edge(c2); res[cnt++]=e2; p[find(edges[e2].a)]=find(edges[e2].b); for(int i=0;i<m && cnt<n-1;i++) { int a=edges[i].a,b=edges[i].b,w=edges[i].w; if(w==c1 || w==c2) { a=find(a),b=find(b); if(a!=b) { res[cnt++]=i; p[a]=b; } } } for(int i=0;i<cnt;i++) { cout<<edges[res[i]].a<<' '<<edges[res[i]].b<<endl; } return 0; } cout<<-1<<endl; return 0; }

其实有心的人已经发现了,这次的题解是AI帮我写的,但是代码都是我自己比赛时写的,有时候自己说不清楚,借AI之口让大家明白,何乐而不为呢!

http://www.jsqmd.com/news/885483/

相关文章:

  • taotoken token plan套餐在实际开发中的成本节省感受
  • 主流源代码管理工具介绍
  • 如何在Windows 11上免费安装安卓子系统:完整简易指南
  • 为学术研究项目构建可复现且成本可控的大模型实验平台
  • NS-USBLoader终极指南:一站式Switch文件传输与RCM注入解决方案
  • 从XP盗版泛滥到Win11强制联网:聊聊微软这二十年是怎么用KMS等机制‘围剿’盗版的
  • 一份来自 Karpathy 的 AI 编程 skill
  • 文档地狱求生指南:从“缺失、过时、晦涩”到“清晰、准确、可用”的技术文档治理实战
  • 小龙虾OpenClaw 全方位实战指南:下载、安装、配置豆包 API Key 与高阶使用技巧
  • 从零开始:Icarus Verilog 开源硬件仿真器完全指南 [特殊字符]
  • 基于FakeAVCeleb数据集的多模态深度伪造检测系统开发:从数据预处理到模型部署的完整指南FakeAVCeleb音频视频多模态数据集的训练和测试
  • 短视频矩阵系统的技术演进:当AI Agent重新定义全域内容运营
  • Video2X终极指南:如何用AI实现专业级视频超分辨率与无损放大
  • 零阶优化:超越梯度下降的神经网络训练新范式
  • ESXi 8.0 运维实战:从硬件RAID卡驱动更新到NTP时间同步,一篇搞定日常管理
  • 突破性架构革命:RPFM如何用Rust+Qt6重塑Total War模组开发范式
  • 从54M到300M:手把手教你用IxChariot搞定802.11n工业网关的极限吞吐量测试
  • 一些SVG小图标去哪里找
  • 投资者网:2026年GEO服务商五强:领航者的制胜逻辑与实战分析 - 罗兰艺境GEO
  • 终极惠普OMEN游戏本性能优化指南:免费开源工具OmenSuperHub完整使用教程
  • 企业网盘怎么选?2026 年 10 款团队协作工具对比
  • 2026.05.24cpp学习内容
  • DyberPet桌面宠物框架:打造属于你的数字伙伴,让桌面互动更有温度
  • 告别卡顿!用Nginx+图新地球+CesiumLab搭建本地离线地图服务(附完整配置代码)
  • 气体涡轮流量计厂家排行榜 - 仪表品牌榜
  • 小白也能秒懂!CSS三种定位方式,看完就能上手写
  • 红包墙公众号管理系统平台
  • 如何快速将B站缓存视频转为MP4:3步实现永久保存的终极免费工具
  • “烟花第一股”ST熊猫终止上市
  • 保姆级教程:在Ubuntu 22.04上搞定NVIDIA驱动、Anaconda和CUDA 12.4(含常见报错解决)