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

【二分】-----【Music Notes S】

P2969 [USACO09DEC] Music Notes S

题目链接

题目描述

FJ 准备教他的奶牛们演奏一首歌曲。这首歌由 N N N 个音符组成,其中第 i i i 个音符持续 B i B_i Bi 个节拍( 1 ≤ N ≤ 50 , 000 1 \leq N \leq 50,000 1N50,000 1 ≤ B i ≤ 10 , 000 1 \leq B_i \leq 10,000 1Bi10,000),因此整首歌的长度不会超过 500,000,000 个节拍。奶牛们将在时间 0 开始演奏这首歌,因此它们将在时间 0 到时间 B 1 B_1 B1 之前演奏第 1 个音符,在时间 B 1 B_1 B1 到时间 B 1 + B 2 B_1 + B_2 B1+B2 之前演奏第 2 个音符,依此类推。

然而,最近奶牛们对这首歌失去了兴趣,因为它们觉得这首歌太长且无聊。因此,为了确保奶牛们集中注意力,他向它们提出了 Q Q Q 个问题( 1 ≤ Q ≤ 50 , 000 1 \leq Q \leq 50,000 1Q50,000),问题的形式为「在时间 T T T 到时间 T + 1 T+1 T+1 之前的区间内,你应该演奏哪个音符?」奶牛们需要你的帮助来回答这些问题,这些问题以 T i T_i Ti 的形式给出( 0 ≤ T i ≤ end_of_song 0 \leq T_i \leq \text{end\_of\_song} 0Tiend_of_song)。

考虑这首由三个音符组成的歌曲,音符的持续时间分别为 2、1 和 3 个节拍:

Beat:   0    1    2    3    4    5    6    ...|----|----|----|----|----|----|--- ...1111111111     :              :22222:              :333333333333333:

这里有一组五个查询及其对应的答案:

Query    Note2        23        34        30        11        1

输入格式

* 第 1 行:两个用空格分隔的整数: N N N Q Q Q

* 第 2 行到第 N + 1 N+1 N+1 行:第 i + 1 i+1 i+1 行包含一个整数: B i B_i Bi

* 第 N + 2 N+2 N+2 行到第 N + Q + 1 N+Q+1 N+Q+1 行:第 N + i + 1 N+i+1 N+i+1 行包含一个整数: T i T_i Ti

输出格式

* 第 1 行到第 Q Q Q 行:对于每个查询,输出一个整数,表示奶牛们应该演奏的音符的索引。

输入输出样例 #1

输入 #1

3 5 
2 
1 
3 
2 
3 
4 
0 
1

输出 #1

2 
3 
3 
1 
1

题解1(前缀和+手写二分)

一 、解题思路

1.前缀和建模

我们可以将每个音符所占的时间段看作一个区间:

  • 1 1 1 个音符对应时间区间为 [ 0 , B 1 ) [0, B_1) [0,B1)
  • 2 2 2 个音符对应时间区间为 [ B 1 , B 1 + B 2 ) [B_1, B_1 + B_2) [B1,B1+B2)
  • 3 3 3 个音符对应时间区间为 [ B 1 + B 2 , B 1 + B 2 + B 3 ) [B_1 + B_2, B_1 + B_2 + B_3) [B1+B2,B1+B2+B3)

所以,我们可以维护一个前缀和数组 sum,其中:

sum[0] = 0
sum[i] = sum[i - 1] + B[i]

表示第 i i i 个音符结束的时间点(不包含)。

2.查询模型转化

对于每个询问时间 T T T,我们需要找到第一个满足 sum[i] > T 的下标 i,也就是说 T T T 落在第 i 个音符的时间区间 [ s u m [ i − 1 ] , s u m [ i ] ) [sum[i-1], sum[i]) [sum[i1],sum[i]) 中。

这是一个典型的有序数组中找第一个大于某值的位置的问题,可以使用手写二分解决。


四、完整代码:

#include<bits/stdc++.h>
using namespace std;int main()
{int n, q;cin >> n >> q;// nums[i] 表示第 i 个音符的持续节拍数,0号位占位(不使用)vector<int> nums(n + 1, 0);for (int i = 1; i <= n; i++){cin >> nums[i]; // 读入每个音符的持续时间}// 前缀和数组 sum[i] 表示前 i 个音符总共持续的节拍数// 即 sum[i] 表示第 i 个音符结束的时间(不包含)vector<int> sum(n + 1);for (int i = 1; i <= n; i++){sum[i] = sum[i - 1] + nums[i];}int a; // 用于接收每个查询时间 T_iwhile (q--){cin >> a;// 手写二分查找,找第一个满足 sum[mid] > a 的下标int l = 1, r = n;int ender = 0; // 记录答案(即对应的音符编号)while (l <= r){int mid = l + (r - l) / 2;if (sum[mid] > a){// 如果 sum[mid] > a,说明第 mid 个音符包含时间 a// 但是我们不能立刻返回 mid,因为还需要确保它是第一个满足条件的// 所以我们先记录答案,继续向左逼近ender = mid;r = mid - 1; // 继续在左半边查找}else{// 否则时间 a 还在 mid 之后的音符中l = mid + 1;}}// 输出结果(第几个音符)cout << ender << endl;}return 0;
}

五、复杂度分析

  • 预处理前缀和: O ( n ) O(n) O(n)
  • 每次查询使用二分: O ( l o g n ) O(log n) O(logn),总共 q q q 次查询
  • 总时间复杂度: O ( n + q l o g n ) O(n + q log n) O(n+qlogn),对于 5 × 1 0 4 5 \times 10^4 5×104 的数据规模完全可以接受

六、总结

1.本题本质上是一个典型的「查询区间在哪段区间中」的问题:

  • 使用前缀和构造每个音符的结束时间
  • 将每个查询转化为在有序数组中找第一个大于某值的位置
  • 使用手写二分提高查询效率

2.这种技巧广泛应用于:

  • 数轴上落点归属区间判断
  • 前缀统计
  • 排序 + 查询的经典模型

3.如需 STL 简洁写法,可以使用:

// 使用 upper_bound 在前缀和数组中查找第一个大于 t 的位置
// sum.begin() + 1:表示从 sum[1] 开始查找(sum[0] = 0 为占位)
// sum.end():表示查找到 sum[n](不含 sum[n+1])
// t:目标时间,我们要找第一个满足 sum[i] > t 的 i
// 返回的是指针(迭代器),需要减去 sum.begin() 转换成下标
int idx = upper_bound(sum.begin() + 1, sum.end(), t) - sum.begin();  // 输出下标 idx,即时间 t 所在的音符编号(对应区间 [sum[idx - 1], sum[idx]))
cout << idx << endl;
http://www.xdnf.cn/news/1079119.html

相关文章:

  • 【Git】同时在本地使用多个github账号进行github仓库管理
  • 通过Curtain 解决方案保障BIM模型安全共享—建筑业的防泄密实战
  • react-打包和本地预览 ——打包优化
  • 【数据结构】C++的unordered_map/set模拟实现(开散列(哈希桶)作底层)
  • npm 命令入门指南(前端小白版)
  • contenteditable网页富文本编辑无法选中图片
  • 从0到1实战!用Docker部署Qwerty Learner输入法的完整实践过程
  • curl for android
  • Linux多线程(十三)之【POSIX信号量基于环形队列的生产消费模型】
  • OpenCV CUDA模块设备层-----在 GPU上高效地执行两个uint类型值的最小值比较函数vmin2()
  • LeetCode 317 最短距离选址问题详解(Swift 实现 + BFS 多源遍历)
  • 高边驱动 低边驱动
  • 多模态AI Agent技术栈解析:视觉-语言-决策融合的算法原理与实践
  • Kafka生态整合深度解析:构建现代化数据架构的核心枢纽
  • JA3指纹在Web服务器或WAF中集成方案
  • 专题:2025即时零售与各类人群消费行为洞察报告|附400+份报告PDF、原数据表汇总下载
  • MacOS Safari 如何打开F12 开发者工具 Developer Tools
  • 打造一个可维护、可复用的前端权限控制方案(含完整Demo)
  • 请求未达服务端?iOS端HTTPS链路异常的多工具抓包排查记录
  • 【CSS揭秘】笔记
  • 网络基础(3)
  • HTML初学者第二天
  • 利用tcp转发搭建私有云笔记
  • Chart.js 安装使用教程
  • 【强化学习】深度解析 GRPO:从原理到实践的全攻略
  • 怎样理解:source ~/.bash_profile
  • vscode vim插件示例json意义
  • 电子电气架构 --- SOVD功能简单介绍
  • 如何系统性评估运维自动化覆盖率:方法与关注重点
  • Spark流水线数据探查组件