1 条题解
-
0
题目详细题解
一、题目核心分析
1. 关键定义
- 几乎必然相等:修改其中一个多重集至多一个元素后,两个多重集完全相等。
- 操作规则:对数组的元素仅能执行减1操作,数组固定不变,操作次数为减少的总数值。
- 问题要求:对每个前缀长度(),求使和的多重集几乎必然相等的最少操作次数。
2. 转化问题
设对于前缀,,表示将完全变成的总操作次数。 根据“几乎必然相等”的定义,我们可以保留一个元素不被压缩到(即该元素的操作次数为),或调整一个元素的取值,使得剩余元素的操作次数总和最小。 最优策略是:找到一个最大的区间,使得该区间覆盖的“可节省操作次数”最大,最终答案为(为最大可节省的操作次数)。
二、算法思路
1. 核心观察
每个元素对应区间,区间长度是该元素的总操作次数。 将这些区间合并重叠/相邻的区间后,最大的合并区间长度即为(因为保留这个区间对应的元素不压缩,能节省最多操作次数)。
2. 算法步骤
- 计算总操作次数:对每个前缀,累加。
- 区间合并:用有序集合维护不相交的区间,插入当前元素的并合并重叠区间。
- 维护最大区间长度:记录合并后最大的区间长度。
- 计算答案:每个前缀的答案为(总操作次数减去最大可节省的操作次数)。
三、代码逐行解析
#include <bits/stdc++.h> using namespace std; using int64 = long long; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); // 加速输入输出 int T; cin >> T; while (T--) { int n; cin >> n; vector<int64> a(n), b(n); for (auto &x: a) cin >> x; for (auto &x: b) cin >> x; std::set<pair<int64,int64>> segs; // 存储不相交的区间[l, r],按l有序 int64 sum = 0, best = 0; for (int i = 0; i < n; ++i) { int64 l = b[i], r = a[i]; sum += r - l; // 累加当前前缀的总操作次数 // 找到可能与当前区间重叠的第一个区间 auto it = segs.lower_bound({l, -4e18}); if (it != segs.begin()) { auto prev = std::prev(it); if (prev->second >= l) it = prev; // 前一个区间与当前区间重叠,调整迭代器 } // 合并所有重叠的区间 while (it != segs.end() && it->first <= r && it->second >= l) { l = std::min(l, it->first); // 更新合并后的左端点 r = std::max(r, it->second); // 更新合并后的右端点 it = segs.erase(it); // 删除被合并的区间 } segs.insert({l, r}); // 插入合并后的新区间 best = std::max(best, r - l); // 更新最大区间长度 // 输出当前前缀的答案 cout << (sum - best) << (i + 1 == n ? '\n' : ' '); } } return 0; }关键细节
- 数据类型:使用(即)避免的数值溢出。
- 区间合并效率:的插入、删除、查找操作均为(为当前区间数),总时间复杂度为,满足的限制。
- 的作用:快速定位第一个左端点的区间,减少合并的遍历次数。
四、复杂度分析
- 时间复杂度:单组测试用例,所有测试用例总复杂度(为所有测试用例的之和)。
- 空间复杂度:,用于存储数组和区间集合。
五、示例验证
假设输入:
1 3 5 7 9 2 3 4- :,区间,,,答案。
- :,区间和合并为,,,答案。
- :,区间和合并为,,,答案。
输出:
0 2 5,符合逻辑。
- 1
信息
- ID
- 6452
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者