1 条题解
-
0
题目理解
我们有一个长度为 的序列 ,初始全为 。
有 个操作 ,表示将 到 都加上 。
有 次查询,每次给出 ,问依次执行第 到第 个操作后,序列的最大值是多少。
问题分析
关键观察
执行操作 后,序列的前缀和变化:
- 位置 到 :
- 位置 到 :不变
因此,最终 的值等于所有覆盖了位置 的操作的 值之和。
即:
我们要找:
$$\max_{i=1}^n a_i = \max_{i=1}^n \sum_{j=l}^{r} [x_j \ge i] \cdot y_j $$
核心思路
1. 问题转化
令 ,则:
- 包含所有操作(因为 对所有 成立)
所以 是一个前缀和递减的序列。
我们要找的就是 。
2. 数据结构设计
struct info_t { ll mx, sum; };mx:该区间操作序列产生的最大值sum:该区间操作序列对 的贡献(即所有 之和)
合并规则:
info_t unite(const info_t &lhs, const info_t &rhs) { return {max(lhs.mx + rhs.sum, rhs.mx), lhs.sum + rhs.sum}; }这表示:
- 总最大值 = max(左区间最大值 + 右区间总和, 右区间最大值)
- 总和 = 左区间总和 + 右区间总和
3. 分块处理
代码将操作序列分成大小为 的块,对每个块预处理信息。
对于每个块:
- 按 排序操作
- 预处理出对于任意区间 在该块内的答案
算法流程详解
1. 预处理阶段
sort(id + 1, id + n + 1, [](int p, int q) { return a[p] < a[q] || (a[p] == a[q] && b[p] > b[q]); });按 排序, 相同时按 降序排列。
2. 线段树构建
void build(int p, int l, int r) { info[p].assign(r - l + 2, vector<info_t>(r - l + 2, {-infll, 0})); // 为每个节点分配存储空间 }3. 关键操作:pushup
void pushup(int p) { int lp = ls(p), rp = rs(p); // 合并左右子节点的关键点(x值) mp[p].resize(mp[lp].size() + mp[rp].size() - 1); merge(mp[lp].begin(), mp[lp].end() - 1, mp[rp].begin(), mp[rp].end() - 1, mp[p].begin()); // 预处理指针数组,用于快速查找 for (int i = 0, li = 0, ri = 0; i < (int)mp[p].size(); ++i) { while (li < (int)mp[lp].size() && mp[lp][li] < mp[p][i]) ++li; while (ri < (int)mp[rp].size() && mp[rp][ri] < mp[p][i]) ++ri; psl[i] = li, psr[i] = ri; } // 预处理所有区间组合的答案 for (int i = 0; i < (int)mp[p].size(); ++i) { for (int j = i + 1; j < (int)mp[p].size(); ++j) { info[p][i][j] = unite(info[lp][psl[i]][psl[j]], info[rp][psr[i]][psr[j]]); } } }4. 查询处理
对于每个查询 :
for (int i = 1; i <= n; i += 512) { update(i); // 更新当前块的信息 for (int j = 1; j <= q; ++j) { ans[j] = unite(ans[j], info[1][pos[l]][pos[r + 1]]); } }
样例分析
对于样例输入:
6 5 6 4 2 6 5 -5 3 6 1 2 3 6 1 6 1 6 2 6 2 6 5 6操作序列:
- (6,4)
- (2,6)
- (5,-5)
- (3,6)
- (1,2)
- (3,6)
查询 [1,6] 的计算:
- (没有的操作)
- (操作2的)
- (操作4,6的)
- (操作3的)
- (操作1的)
最大值为 ,与输出一致。
复杂度分析
- 预处理:,其中
- 查询:
- 总复杂度:,取 时为
对于 ,可接受。
算法标签
- 分块
- 线段树
- 离线查询
- 前缀和优化
- 区间最值
总结
这道题的解法核心是:
- 问题转化:发现序列最大值等于某个前缀和序列的最大值
- 分块处理:将操作序列分块,预处理每个块的信息
- 高效合并:设计合适的数据结构支持区间信息快速合并
- 边界处理:正确处理不完整块的情况
通过分块平衡了预处理和查询的复杂度,实现了高效求解。
- 1
信息
- ID
- 4015
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 10
- 已通过
- 1
- 上传者