news 2026/10/27 3:15:33

OI经典题解析:高效处理数组颜色查询与交换操作

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OI经典题解析:高效处理数组颜色查询与交换操作

1. 项目背景与题目解析

P3939 "数颜色"是信息学奥林匹克竞赛(OI)中一道经典的数组操作题目。题目要求我们处理一个颜色序列,支持两种操作:查询某个区间内特定颜色的数量,以及交换相邻两个元素的位置。这类问题在实际编程竞赛中非常常见,考察选手对基础数据结构的掌握和算法优化能力。

题目具体描述如下:

  • 给定一个长度为n的颜色序列(颜色用正整数表示)
  • 进行m次操作,操作分为两种类型:
    1. 查询操作:询问区间[l,r]内颜色为c的元素个数
    2. 交换操作:交换位置x和x+1的元素

对于100%的数据,n,m ≤ 3×10^5,要求算法时间复杂度控制在O(mlogn)或更好。

2. 基础解法与性能分析

2.1 暴力解法及其局限性

最直观的解法是直接模拟题目要求的操作:

// 暴力解法示例 vector<int> colors(n); for(int i=0; i<m; i++){ if(op == 1){ // 查询操作 int cnt = 0; for(int j=l; j<=r; j++){ if(colors[j] == c) cnt++; } cout << cnt << endl; } else { // 交换操作 swap(colors[x], colors[x+1]); } }

这种暴力解法的时间复杂度为:

  • 查询操作:O(r-l+1)
  • 交换操作:O(1)

对于n,m都是3×10^5的情况,最坏情况下(每次都是查询整个区间),总时间复杂度会达到O(mn)=9×10^10,显然无法在合理时间内完成。

2.2 数据结构选型思考

我们需要选择合适的数据结构来优化查询操作。常见的选择有:

  1. 线段树:可以维护区间信息,但直接维护每种颜色的计数会导致空间复杂度过高
  2. 树状数组:适合维护前缀和,但同样面临颜色种类多的问题
  3. 平衡树/STL set:可以对每种颜色维护位置集合,查询时使用lower_bound
  4. 分块处理:将序列分成若干块,每块维护颜色计数

经过分析,使用STL vector存储每种颜色的位置集合是最优选择:

  • 空间复杂度:O(n)
  • 查询操作:二分查找O(logn)
  • 交换操作:更新两个元素的位置O(logn)

3. 高效解法实现

3.1 数据结构设计

我们为每种颜色维护一个有序的位置集合:

unordered_map<int, vector<int>> color_pos; // 颜色到位置列表的映射 vector<int> colors(n); // 原始颜色序列

初始化时需要预处理:

for(int i=0; i<n; i++){ cin >> colors[i]; color_pos[colors[i]].push_back(i+1); // 假设位置从1开始编号 }

3.2 查询操作实现

查询区间[l,r]内颜色c的数量,可以通过两次二分查找实现:

auto& pos = color_pos[c]; int left = lower_bound(pos.begin(), pos.end(), l) - pos.begin(); int right = upper_bound(pos.begin(), pos.end(), r) - pos.begin(); cout << right - left << endl;

3.3 交换操作实现

交换位置x和x+1的元素需要注意:

  1. 如果两个位置颜色相同,无需操作
  2. 否则需要更新两个颜色的位置集合
if(colors[x-1] == colors[x]) continue; // 位置从1开始编号 int c1 = colors[x-1], c2 = colors[x]; auto& vec1 = color_pos[c1]; auto& vec2 = color_pos[c2]; // 在c1的位置集合中找到x并改为x+1 auto it1 = lower_bound(vec1.begin(), vec1.end(), x); *it1 = x+1; // 在c2的位置集合中找到x+1并改为x auto it2 = lower_bound(vec2.begin(), vec2.end(), x+1); *it2 = x; // 交换实际颜色值 swap(colors[x-1], colors[x]);

3.4 完整代码实现

#include <iostream> #include <vector> #include <algorithm> #include <unordered_map> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<int> colors(n); unordered_map<int, vector<int>> color_pos; for(int i=0; i<n; i++){ cin >> colors[i]; color_pos[colors[i]].push_back(i+1); // 位置从1开始 } while(m--){ int op; cin >> op; if(op == 1){ int l, r, c; cin >> l >> r >> c; auto& pos = color_pos[c]; int left = lower_bound(pos.begin(), pos.end(), l) - pos.begin(); int right = upper_bound(pos.begin(), pos.end(), r) - pos.begin(); cout << right - left << endl; } else { int x; cin >> x; if(x < 1 || x >= n || colors[x-1] == colors[x]) continue; int c1 = colors[x-1], c2 = colors[x]; auto& vec1 = color_pos[c1]; auto& vec2 = color_pos[c2]; auto it1 = lower_bound(vec1.begin(), vec1.end(), x); *it1 = x+1; auto it2 = lower_bound(vec2.begin(), vec2.end(), x+1); *it2 = x; swap(colors[x-1], colors[x]); } } return 0; }

4. 算法优化与性能分析

4.1 时间复杂度分析

  • 预处理:O(n),只需遍历一次序列
  • 查询操作:O(logn),两次二分查找
  • 交换操作:O(logn),两次二分查找加修改

总时间复杂度为O((n+m)logn),完全满足题目要求。

4.2 空间复杂度分析

使用unordered_map和vector存储位置信息,空间复杂度为O(n),可以接受。

4.3 进一步优化思路

  1. 输入输出优化:使用ios::sync_with_stdio(false)和cin.tie(nullptr)加速IO
  2. 内存分配优化:预先估计颜色种类,reserve足够空间
  3. 并行处理:如果查询操作很多,可以考虑多线程处理独立查询

5. 常见错误与调试技巧

5.1 边界条件处理

  • 位置编号从0还是1开始要保持一致
  • 交换操作时x的有效范围检查
  • 相同颜色交换时的特判

5.2 数据结构选择误区

  1. 误用multiset:虽然可以维护位置,但修改操作时间复杂度为O(n)
  2. 线段树过度设计:对于这种特定问题,线段树实现复杂且性能未必更优
  3. 忽略二分查找的前提:必须保证位置数组是有序的

5.3 调试建议

  1. 小数据测试:先用手算能验证的小数据测试
  2. 输出中间结果:在交换操作前后打印颜色和位置信息
  3. 对拍测试:用暴力算法生成随机数据对比结果

6. 竞赛中的实际应用

这类问题在竞赛中常见的变体包括:

  1. 带修改的区间查询:如区间赋值、区间加等
  2. 多维查询:如二维平面上的颜色统计
  3. 动态版本维护:支持回退到历史版本

掌握这种基于颜色分类和二分查找的技巧,可以解决许多类似问题。在实际比赛中,快速识别问题本质并选择合适的数据结构是关键。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 3:15:19

PyTorch可信环境构建:安装验证五层穿透式压力测试

1. 为什么“PyTorch安装与验证”是深度学习入门第一道真实门槛 很多人以为装个PyTorch就是 pip install torch 敲一行命令的事——直到他在Windows上卡在“无法验证此设备所需的驱动程序的数字签名”&#xff0c;在Mac上遇到 ERROR: Could not find a version that satisfi…

作者头像 李华
网站建设 2026/10/27 3:14:48

数据库程序操作优化:提升性能的四大核心策略

1. 程序操作优化在数据库性能中的核心地位数据库性能优化从来都不是单一维度的技术活&#xff0c;而程序操作优化恰恰是最容易被忽视的关键环节。我见过太多团队把性能问题简单归咎于硬件配置或索引缺失&#xff0c;却忽略了那些隐藏在代码中的性能杀手。程序操作优化本质上是对…

作者头像 李华
网站建设 2026/10/27 3:14:47

Transformer架构演进七周年:核心模块改进与工程选型深入解析

从 2017 年《Attention Is All You Need》公开算起&#xff0c;Transformer 架构已经走过了七年多。这七年里&#xff0c;NLP 从 RNN 统治变成了 Transformer 一统天下&#xff0c;CV 从 CNN 一统天下变成了 ViT 分庭抗礼&#xff0c;多模态、推荐系统、甚至蛋白质结构预测都开…

作者头像 李华
网站建设 2026/10/27 3:14:26

Python虚拟环境本质:venv与conda的底层原理与工程实践

1. 为什么你每次新建项目都要重装一遍 numpy&#xff1f;——虚拟环境不是“可选配件”&#xff0c;而是Python项目的呼吸系统我第一次在公司接手一个老项目时&#xff0c;只花了15分钟就把它搞崩了。不是代码写错了&#xff0c;也不是逻辑有问题&#xff0c;而是我在自己电脑上…

作者头像 李华
网站建设 2026/10/27 3:14:21

MoonBit:用可执行代码替代AI截图的原型新范式

1. 为什么“AI生成截图”正在毁掉产品设计的根基最近在三个不同团队的原型评审会上&#xff0c;我连续看到同一幕&#xff1a;产品经理把一张标注着“用户登录流程”的PNG截图投到大屏上&#xff0c;说“这就是我们下个迭代要做的”。可当开发同事放大到200%时&#xff0c;发现…

作者头像 李华
网站建设 2026/10/27 3:13:30

综合能源系统中替代型需求响应的优化实践

1. 综合能源系统的时代背景与核心挑战现代能源系统正经历着从传统单一能源供应向多能互补模式的深刻转型。我清晰地记得2018年参与某工业园区能源改造项目时&#xff0c;第一次亲眼目睹了电、热、气多种能源流在同一个控制中心被协调调度的场景。这种打破能源品类壁垒的系统集成…

作者头像 李华