算法竞赛进阶指南,215页, 线段树
本题要点:
1、线段树的每个节点维护的信息
线段的左右端点:L 和 R
dat : 区间最大连续子段和
sum : 区间所有数的总和
lmax: 紧靠左端的最大连续子段的总和
rmax: 紧靠右端的最大连续子段的总和
2、线段树 的父节点 p 和左孩子 2 * p, 右孩子 2 * p + 1 的信息关系, 在build 和 change函数中维护好节点的信息
t[p].sum = t[2 * p].sum + t[2 * p + 1].sum;
t[p].lmax = max(t[2 * p].lmax, t[2 * p].sum + t[2 * p + 1].lmax);
t[p].rmax = max(t[2 * p + 1].rmax, t[2 * p + 1].sum + t[2 * p].rmax);
t[p].dat = max(t[p * 2].dat, t[2 * p + 1].dat);
t[p].dat = max(t[p].dat, t[2 * p].rmax + t[2 * p + 1].lmax);
3、查询每一段 [L, R] 的 区间最大连续子段和 时,分类讨论:
区间[L, R] 仅仅与左孩子有交集
区间[L, R] 仅仅与右孩子有交集,
区间[L, R] 同时与左右孩子有交集,形式类似于上面公式;
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MaxN = 500010;
int a[MaxN];
int n, m;
struct segTree
{
int L, R;
int dat; //区间最大连续子段和
int sum; //区间总和
int lmax, rmax;
}t[MaxN * 4];
void build(int p, int L, int R)
{
t[p].L = L, t[p].R = R;
if(L == R) //叶子节点
{
t[p].sum = t[p].lmax = t[p].rmax = t[p].dat = a[L];
return;
}
int mid = (L + R) / 2;
build(p * 2, L, mid); //左子节点
build(p * 2 + 1, mid + 1, R); //右子节点
t[p].sum = t[2 * p].sum + t[2 * p + 1].sum;
t[p].lmax = max(t[2 * p].lmax, t[2 * p].sum + t[2 * p + 1].lmax);
t[p].rmax = max(t[2 * p + 1].rmax, t[2 * p + 1].sum + t[2 * p].rmax);
t[p].dat = max(t[p * 2].dat, t[2 * p + 1].dat);
t[p].dat = max(t[p].dat, t[2 * p].rmax + t[2 * p + 1].lmax);
}
void change(int p, int x, int v)
{
if(t[p].L == t[p].R)
{
t[p].sum = t[p].lmax = t[p].rmax = t[p].dat = v;
return;
}
int mid = (t[p].L + t[p].R) / 2;
if(x <= mid)
{
change(p * 2, x, v);
}else{
change(p * 2 + 1, x, v);
}
t[p].sum = t[2 * p].sum + t[2 * p + 1].sum;
t[p].lmax = max(t[2 * p].lmax, t[2 * p].sum + t[2 * p + 1].lmax);
t[p].rmax = max(t[2 * p + 1].rmax, t[2 * p + 1].sum + t[2 * p].rmax);
t[p].dat = max(t[p * 2].dat, t[2 * p + 1].dat);
t[p].dat = max(t[p].dat, t[2 * p].rmax + t[2 * p + 1].lmax);
}
segTree ask(int p, int L, int R)
{
if(L <= t[p].L && R >= t[p].R)
{
return t[p];
}
segTree ans, left, right;
ans.dat = ans.lmax = ans.rmax = -0x3f3f3f3f; //负无穷
ans.sum = 0;
int mid = (t[p].L + t[p].R) / 2;
if(L <= mid)
{
left = ask(p * 2, L, R);// 左孩子有重叠
ans.sum += left.sum;
ans.dat = max(ans.dat, left.dat);
ans.lmax = left.lmax;
}
if(R > mid)
{
right = ask(p * 2 + 1, L, R);//右孩子有重叠
ans.sum += right.sum;
ans.dat = max(ans.dat, right.dat);
ans.rmax = right.rmax;
}
if(L <= mid && R > mid) //左右孩子均有重叠
{
ans.lmax = max(left.sum + right.lmax, ans.lmax);
ans.rmax = max(right.sum + left.rmax, ans.rmax);
ans.dat = max(ans.dat, left.rmax + right.lmax);
}
return ans;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
}
build(1, 1, n);
int cmd, x, y;
for(int i = 0; i < m; ++i)
{
scanf("%d%d%d", &cmd, &x, &y);
if(1 == cmd)
{
if(x > y)
{
swap(x, y);
}
printf("%d\n", ask(1, x, y).dat);
}else{
change(1, x, y);
}
}
return 0;
}
/*
5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 3 2
*/
/*
2
-1
*/