CH 4301 Can you answer on these queries III(进阶指南,线段树)

算法竞赛进阶指南,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
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值