「NOI2010」超级钢琴
知识点:RMQ,堆,小技巧
有趣的套路。
简述
给定一长度为 的数列 ,给定参数 ,求最大的 个长度在 间的子段之和。
,,,保证有解。
2S,512MB。
分析
一种显然的暴力是枚举所有长度在 内的区间,大力求和并放入大根堆中,取最优的前 个即可。复杂度 级别。期望得分 20pts。
而 同阶,上述做法的问题主要在于堆中有许多无贡献的元素。考虑如何缩小堆的大小,但又能保证每次访问堆顶时取出的元素是最优的。本题提供了一种解决此类前 优问题的思路。
首先求得 的前缀和 ,任一区间都可以表示成两个前缀相减的形式,则题目所求即为最大的 个 的值。若左端点 固定,最优的右端点 可以通过对 建 ST 表查询区间 的最大值 求得。
对于上述过程,定义状态 表示左端点为 ,查询区间为 ,在此区间内找到的最优的 为 , 的值是 。
初始时枚举所有左端点 ,按照上述 ST 表做法构造初始状态 并放入以状态中 为关键字的大根堆中。每次取出堆顶的状态,统计其贡献 ,然后构造新状态 与 放入堆中。可以发现这样能够保证枚举到所有有贡献的状态。
堆中初始有 个状态,每次查询分裂状态都会使堆的大小 ,总复杂度 级别。
实现时注意一些边界细节。
代码
复制复制//知识点:RMQ,堆,小技巧 /* By:Luckyblock */ #include <algorithm> #include <cctype> #include <cstdio> #include <cstring> #include <queue> #define pr std::pair #define mp std::make_pair #define LL long long const int kN = 5e5 + 10; //============================================================= struct Data { LL val; int i, pos, l, r; bool operator < (const Data &sec_) const { return val < sec_.val; } }; int n, k, l, r; LL ans, sum[kN]; std::priority_queue <Data> q; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void Chkmax(int &fir, int sec) { if (sec > fir) fir = sec; } void Chkmin(int &fir, int sec) { if (sec < fir) fir = sec; } namespace ST { const int kMaxn = kN; const int kMaxLog = 20 + 1; LL mx[kMaxn][kMaxLog]; int Log2[kMaxn], pos[kMaxn][kMaxLog]; void Build() { for (int i = 0; i <= n; ++ i) { if (i > 1) Log2[i] = Log2[i >> 1] + 1; mx[i][0] = sum[i]; pos[i][0] = i; } for (int i = 1; i <= kMaxLog; ++ i) { for (int j = 0; j + (1 << i) - 1 <= n; ++ j) { if (mx[j][i - 1] >= mx[j + (1 << (i - 1))][i - 1]) { mx[j][i] = mx[j][i - 1]; pos[j][i] = pos[j][i - 1]; } else { mx[j][i] = mx[j + (1 << (i - 1))][i - 1]; pos[j][i] = pos[j + (1 << (i - 1))][i - 1]; } } } } int Query(int L_, int R_) { int lth = Log2[R_ - L_ + 1]; return mx[L_][lth] > mx[R_ - (1 << lth) + 1][lth] ? pos[L_][lth]: pos[R_ - (1 << lth) + 1][lth]; } } //============================================================= int main() { n = read(), k = read(), l = read(), r = read(); for (int i = 1; i <= n; ++ i) sum[i] = sum[i - 1] + read(); ST::Build(); for (int i = 0; i <= n; ++ i) { if (i + l > n) break; //无贡献 int pos = ST::Query(i + l, std::min(n, i + r)); q.push((Data) {sum[pos] - sum[i], i, pos, i + l, std::min(n, i + r)}); //注意取 min } while (k --) { Data t = q.top(); q.pop(); ans += t.val; int i = t.i, pos = t.pos, l_ = t.l, r_ = t.r; int new_pos = ST::Query(l_, pos - 1); if (l_ <= pos - 1) q.push((Data) {sum[new_pos] - sum[i], i, new_pos, l_, pos - 1}); new_pos = ST::Query(pos + 1, r_); if (pos + 1 <= r_) q.push((Data) {sum[new_pos] - sum[i], i, new_pos, pos + 1, r_}); } printf("%lld\n", ans); return 0; }
作者@Luckyblock,转载请声明出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】