P4513 小白逛公园 区间合并
解题思路
这道题需要维护一个动态变化的数组,并支持两种操作:修改某个元素的值和查询区间内最大连续子段和。使用线段树可以高效解决这个问题。
关键点:
-
线段树节点设计:每个节点需要维护四个信息:
-
maxx:当前区间的最大子段和 -
sum:当前区间的总和 -
ls:当前区间从左端点开始的最大子段和 -
rs:当前区间从右端点开始的最大子段和
-
-
信息合并:合并左右子区间的信息时需要考虑多种情况:
-
最大子段和可能在左子区间、右子区间或跨越两个子区间
-
从左端点开始的最大子段和可能是左子区间的
ls,也可能是左子区间的sum加上右子区间的ls -
从右端点开始的最大子段和同理
-
-
查询处理:查询时需要合并多个区间的信息,处理方式与
pushup类似
代码注释
#include<bits/stdc++.h> #define f(i,s,e) for(int i = s; i <= e; i++) // 定义循环宏 #define lc rt << 1 // 左子节点索引 #define rc rt << 1 | 1 // 右子节点索引 #define lson lc,l,mid // 左子树参数 #define rson rc,mid + 1,r // 右子树参数 #define ll long long // 定义长整型别名 using namespace std; const ll N = 1e6+10; const int inf = 1e18; // 线段树节点结构体 struct node { ll maxx; // 当前区间的最大子段和 ll sum; // 当前区间的总和 ll ls; // 从左端点开始的最大子段和 ll rs; // 从右端点开始的最大子段和 }; node t[N << 2]; // 线段树数组 ll a[N], n, m; // 原始数组、元素个数、操作次数 // 更新当前节点的信息 void pushup(int rt) { t[rt].sum = t[lc].sum + t[rc].sum; // 总和是左右子树和相加 t[rt].ls = max(t[lc].ls, t[lc].sum + t[rc].ls); // 左端最大和可能是左子树的ls或左子树sum+右子树ls t[rt].rs = max(t[rc].rs, t[rc].sum + t[lc].rs); // 右端最大和同理 t[rt].maxx = max(t[lc].rs + t[rc].ls, max(t[lc].maxx, t[rc].maxx)); // 最大子段和可能是跨越左右子树或单独在某一侧 } // 构建线段树 void build(int rt, int l, int r) { if(l == r) { // 叶子节点 t[rt].maxx = t[rt].sum = t[rt].ls = t[rt].rs = a[l]; // 初始化所有值为数组元素 return; } int mid = (l + r) >> 1; build(lson); // 构建左子树 build(rson); // 构建右子树 pushup(rt); // 更新当前节点信息 } // 单点修改 void change(int rt, int l, int r, int x, int y) { if(l == r) { // 找到目标位置 t[rt].maxx = t[rt].sum = t[rt].ls = t[rt].rs = y; // 更新所有值 return; } int mid = (l + r) >> 1; if(x <= mid) change(lson, x, y); // 在左子树中修改 if(x > mid) change(rson, x, y); // 在右子树中修改 pushup(rt); // 更新当前节点信息 } // 区间查询 node query(int rt, int l, int r, int x, int y) { if(y < l || r < x) return {-inf, -inf, -inf, -inf}; // 区间无交集,返回无效节点 if(x <= l && r <= y) return t[rt]; // 当前区间完全包含在查询区间内 int mid = (l + r) >> 1; node L = query(lson, x, y); // 查询左子树 node R = query(rson, x, y); // 查询右子树 node res; res.sum = L.sum + R.sum; // 合并总和 res.ls = max(L.ls, L.sum + R.ls); // 合并左端最大和 res.rs = max(R.rs, R.sum + L.rs); // 合并右端最大和 res.maxx = max(L.rs + R.ls, max(L.maxx, R.maxx)); // 合并最大子段和 return res; } int main() { scanf("%d%d", &n, &m); f(i,1,n) scanf("%lld", &a[i]); // 读取初始数组 build(1,1,n); // 构建线段树 ll op, x, y, z; while(m--) { scanf("%lld", &op); if(op == 2) { // 修改操作 scanf("%lld%lld", &x, &y); change(1,1,n,x,y); // 修改指定位置的值 } else { // 查询操作 scanf("%lld%lld", &x, &y); if(x > y) swap(x,y); // 处理a>b的情况 printf("%lld\n", query(1,1,n,x,y).maxx); // 查询并输出最大子段和 } } return 0; }

浙公网安备 33010602011771号