首页 > 极客资料 博客日记
线段树维护最大子段和及其类似问题
2025-01-12 17:00:03极客资料围观5次
这篇文章介绍了线段树维护最大子段和及其类似问题,分享给大家做个参考,收藏极客之家收获更多编程知识
引入
link。
我们可以分析出上题就是带修改的最大子段和。
遇到这种类型的题目应该想到用线段树。
实现
对于原数列,先建起一棵线段树,每个节点包含 最大前缀、最大后缀、最大字段和、区间和 信息。
当你明确一道题是线段树时,要先思考
pushup
和pushdown
怎么写,因为剩下的都是差不多的。 —— jzp.
因为本题是单查,没有 pushdown
,就先考虑 pushup
怎么写:
- 最大前缀只可能是左儿子的最大前缀或是左儿子的和加上右儿子的最大前缀,即 \(maxl_i = \max\{maxl_l, sum_l + maxl_r\}\)。
- 最大后缀同理,\(maxr_i = \max\{maxr_r, sum_r, maxr_l\}\)。
- 最大子段和就是左儿子最大子段和或右儿子最大子段和或左儿子最大后缀加右儿子最大前缀,即 \(maxs_i = \max\{maxs_l, maxs_r, maxr_l + maxl_r\}\)。
- 区间和很简单,不赘述。
void pushup(int id) {
sum(id) = sum(ls) + sum(rs);
maxl(id) = max(maxl(ls), sum(ls) + maxl(rs));
maxr(id) = max(maxr(rs), sum(rs) + maxr(ls));
maxs(id) = max(max(maxs(ls), maxs(rs)), maxr(ls) + maxl(rs));
}
那么对于每一次询问,我们找到线段树上的左右端点 \(l\)、\(r\) 对应的两点 \(p_l\)、\(p_r\)。
当我们从上往下爬树爬到 \(k = LCA(p_l, p_r)\) 时,\(l\) 和 \(r\) 就会分开为两个区间。
此时答案有几种可能:
- \(l \le r \le m\),其中 \(m\) 为该区间的中间点,此时递归左侧得到答案。
- \(m \lt l \le r\),此时递归右侧得到答案。
- \(l \le m \lt r\),此时合并两次得到的答案。
以上三者取最大值返回。
这跟 cdq 分治的思想有异曲同工之妙。
当 \(l\) 与 \(r\) 并没有分叉时,就直接走下去即可。
那么此时查询也可以顺利地写出来了。
segment query(int id, int lft, int rht, int l, int r) { // 这里用 segment 作为返回值是因为每层递归都需要用到下一层递归的结果
if (l <= lft && rht <= r) return seg[id];
int mid = (lft + rht) >> 1;
if (r <= mid) return query(ls, lft, mid, l, r);
if (l > mid) return query(rs, mid + 1, rht, l, r);
segment a = query(ls, lft, mid, l, r), b = query(rs, mid + 1, rht, l, r), t;
t.sum = a.sum + b.sum;
t.maxl = max(a.maxl, a.sum + b.maxl);
t.maxr = max(b.maxr, b.sum + a.maxr);
t.maxs = max(max(a.maxs, b.maxs), a.maxr + b.maxl);
return t;
}
整体代码:
struct segment_tree {
#define ls (id << 1)
#define rs (id << 1 | 1)
#define sum(id) seg[id].sum
#define maxl(id) seg[id].maxl
#define maxr(id) seg[id].maxr
#define maxs(id) seg[id].maxs
struct segment {
int maxl, maxr;
int sum, maxs;
} seg[N << 2];
void pushup(int id) {
sum(id) = sum(ls) + sum(rs);
maxl(id) = max(maxl(ls), sum(ls) + maxl(rs));
maxr(id) = max(maxr(rs), sum(rs) + maxr(ls));
maxs(id) = max(max(maxs(ls), maxs(rs)), maxr(ls) + maxl(rs));
}
void build(int id, int lft, int rht) {
if (lft == rht) {
sum(id) = a[lft];
maxl(id) = maxr(id) = maxs(id) = a[lft];
return;
}
int mid = (lft + rht) >> 1;
build(ls, lft, mid), build(rs, mid + 1, rht);
pushup(id);
}
void change(int id, int lft, int rht, int x, int v) {
// if (lft > x || rht < x) return;
if (lft == rht) {
// a[lft] = v;
sum(id) = v;
maxl(id) = maxr(id) = maxs(id) = v;
return;
}
int mid = (lft + rht) >> 1;
if (x <= mid) change(ls, lft, mid, x, v);
else change(rs, mid + 1, rht, x, v);
pushup(id);
}
segment query(int id, int lft, int rht, int l, int r) {
// if (lft > r || rht < l) return ;
if (l <= lft && rht <= r) return seg[id];
int mid = (lft + rht) >> 1;
if (r <= mid) return query(ls, lft, mid, l, r);
if (l > mid) return query(rs, mid + 1, rht, l, r);
segment a = query(ls, lft, mid, l, r), b = query(rs, mid + 1, rht, l, r), t;
t.sum = a.sum + b.sum;
t.maxl = max(a.maxl, a.sum + b.maxl);
t.maxr = max(b.maxr, b.sum + a.maxr);
t.maxs = max(max(a.maxs, b.maxs), a.maxr + b.maxl);
return t;
}
} seg;
我们可以通过线段树维护最大子段和来推广到其他类似的问题。
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签:
相关文章
最新发布
- .NET 开发的分流抢票软件,不做广告、不收集隐私
- 深入理解ASP.NET Core 管道的工作原理
- DataV Note:让Jupyter Notebook绽放新活力
- Mysql身份认证过程
- .NET 9 new features-Microsoft.ML.Tokenizers 库
- 掌握设计模式--代理模式
- 前端实现 HTML 网页转 PDF 并导出
- 回顾 2024 年 12 个月的C#/.NET/.NET Core优秀项目和框架简报
- 《深入理解Mybatis原理》Mybatis中的缓存实现原理
- 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(一):从.NET IoT入门开始