牛客笔记7.15
问题描述
给定一个长度为 n 的数组,每天增大其中一个数的值。每次操作后,计算有多少数满足:在与其他所有数的比较中,至少输掉了 ⌈(n-1)/2⌉ 次(即该数小于至少一半的其他数)。
核心思路
观察:
- 对于总元素数
n,⌈(n-1)/2⌉ 等价于n - ⌊n/2⌋。 - 例如:
n=5时,⌈(5-1)/2⌉ = 2,即需要小于至少 2 个数。n=6时,⌈(6-1)/2⌉ = 3,即需要小于至少 3 个数。
- 因此,问题转化为:每次操作后,统计严格小于第
m = n - ⌊n/2⌋小元素的数的数量。
- 对于总元素数
数据结构选择:
使用
ordered_set维护动态有序集合。功能:
插入 / 删除元素:O (log n)
查询第 k 小元素:
find_by_order(k)→ O(log n) 查询元素排名:
order_of_key(key)→ O(log n)
算法实现
1 |
|
关键步骤解析
- 初始化阶段:
- 将每个元素存储为
{值, 唯一标识}的形式,确保元素的唯一性。 - 唯一标识通过递增的
id生成,避免值相同的元素冲突。
- 将每个元素存储为
- 查询参数计算:
k = n / 2:计算 ⌊n/2⌋。m = n - k:即n - ⌊n/2⌋,表示需要找到的第m小的元素位置。
- 动态更新:
- 每次更新时,先删除旧值,再插入新值(值增加后的结果)。
- 通过
find_by_order(m)查询第m小的元素的值s。
- 结果统计:
- 使用
order_of_key(pll(s, 0))统计严格小于s的元素数量,即为答案。
- 使用
复杂度分析
- 初始化:O (n log n)(插入 n 个元素)
- 单次查询:O (log n)(删除、插入、查询排名各一次)
- 总体复杂度:O(n log n + q log n)
ordered_set
基于红黑树实现,支持动态维护有序集合。
核心操作:
1 | insert(key) // 插入元素,O(log n) |
ordered_set模板
1 | typedef tree<Key, Mapped, Compare, Tag, Node_Update> ordered_set; |
**
Key**:存储的元素类型(如int,ll,pair<ll, ll>等)。- 示例:
tree<int, null_type, ...>存储整数。
- 示例:
**
Mapped**:null_type:表示这是一个集合(Set)。- 若为
map,可指定映射类型(如tree<int, int, ...>表示键值对)。
**
Compare**:比较函数,定义元素的排序规则。升序排列
1
2
3
4
5struct comp {
bool operator()(const int& a, const int& b) const {
return a < b; // 升序排列
}
};
**
Tag**:底层数据结构类型。rb_tree_tag:红黑树(平衡二叉搜索树)。splay_tree_tag:伸展树(自适应树)。
**
Node_Update**:节点更新策略。tree_order_statistics_node_update:启用顺序统计功能。- 提供两个关键方法:
order_of_key(k):返回小于k的元素数量。find_by_order(k):返回第k小的元素的迭代器(从 0 开始)。
ordered_set 的常用操作
以下是 ordered_set 的核心操作及时间复杂度:
| 操作 | 代码示例 | 时间复杂度 |
|---|---|---|
| 插入元素 | os.insert(key) |
O(log n) |
| 删除元素 | os.erase(key) |
O(log n) |
| 查询元素是否存在 | os.find(key) != os.end() |
O(log n) |
| 查询第 k 小的元素 | *os.find_by_order(k) |
O(log n) |
| 查询小于 key 的元素数量 | os.order_of_key(key) |
O(log n) |
| 查询元素的排名 | os.order_of_key(key) |
O(log n) |
| 获取集合大小 | os.size() |
O(1) |
基础用法
1 |
|
pair 类型
在之前的算法中,我们使用 pair<ll, ll> 存储元素,确保值相同时通过唯一标识区分:
1 | struct comp { |
对比其他数据结构
| 数据结构 | 查询排名 | 查询第 k 小 | 插入 / 删除 | 支持重复元素 |
|---|---|---|---|---|
ordered_set |
O(log n) | O(log n) | O(log n) | 否 |
std::set |
❌ | ❌ | O(log n) | 否 |
std::multiset |
❌ | ❌ | O(log n) | 是 |
| 线段树 | O(log n) | O(log n) | O(log n) | 是 |