0
点赞
收藏
分享

微信扫一扫

线段树求最大子段和问题

舍予兄 2022-04-25 阅读 68

Acwing 245 - 你能回答这些问题吗

题目链接

给定长度为 N N N的序列 A A A,以及 M M M条指令。每条指令可能为以下两种之一:

  1. 1   x   y 1\ x\ y 1 x y,查询区间 [ x , y ] [x,y] [x,y]中的最大连续子段和。
  2. 2   x   y 2\ x\ y 2 x y,把 A [ x ] A[x] A[x]改成 y y y

对于每个查询指令,输出一个整数表示答案。

对于线段树的每一个节点,维护对应区间的最大前缀、最大后缀、区间内的元素和、区间最大子段和。

struct Tree {
	int l, r;
	LL sum, pre, suf, mx;
}t[2000050];

push_up函数:每次更新完两个子区间后,要通过子区间的信息维护当前信息的内容。

  1. 区间元素和sum:直接取两个区间和的和即可。
  2. 区间最大前缀:取左区间最大前缀、左区间元素和+右区间最大前缀中的最大值。
  3. 区间最大后缀:取右区间最大后缀、右区间元素和+左区间最大后缀中的最大值。
  4. 区间最大子段和:取左、右区间最大子段和、左区间最大后缀+右区间最小前缀三者中的最大值。
void pu(int ni) {
	t[ni].sum = t[ni << 1].sum + t[ni << 1 | 1].sum;
	t[ni].pre = max(t[ni << 1].pre, t[ni << 1].sum + t[ni << 1 | 1].pre);
	t[ni].suf = max(t[ni << 1 | 1].suf, t[ni << 1].suf + t[ni << 1 | 1].sum);
	t[ni].mx = max({t[ni << 1].mx, t[ni << 1 | 1].mx, t[ni << 1].suf + t[ni << 1 | 1].pre});
}

建树:
注意维护的四个值的初始化。

void build_tree(int ni, int l, int r) {
	t[ni].l = l; t[ni].r = r;
	if (l == r) {
		t[ni].sum = t[ni].pre = t[ni].suf = t[ni].mx = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build_tree(ni << 1, l, mid);
	build_tree(ni << 1 | 1, mid + 1, r);
	pu(ni);
}

更新操作:单点修改,所以没有懒标记,也不需要push_down函数。
单点修改后push_up即可。

void fix(int ni, int pos, int x) {
	if (t[ni].l == t[ni].r and t[ni].l == pos) {
		t[ni].sum = t[ni].pre = t[ni].suf = t[ni].mx = x;
		return;
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	if (pos <= mid) fix(ni << 1, pos, x);
	else fix(ni << 1 | 1, pos, x);
	pu(ni); 
}

查询操作:查询操作稍微复杂一些,因为涉及到查询区间和当前区间的相对关系。

  1. 如果查询区间包含了当前区间,那么返回当前区间的全部四个信息。(为了方便起见,直接返回线段树的节点。)
  2. 如果查询区间完全在当前区间的左半区间,则只查询左半区间并返回所得节点信息。
  3. 如果查询区间完全在当前区间的右半区间,则只查询右半区间并返回所得节点信息。
  4. 如果查询区间横跨了左右区间,那么分别查询左右两个区间。由于是不断递归获得的信息,所以虽然查询了左右两个区间,但是获得的信息只有我们查询区间的信息。使用与pu函数相同的思想对查询结果进行push_up,将push_up出来的结果进行返回。
Tree query(int ni, int l, int r) {
	if (l <= t[ni].l and t[ni].r <= r) {
		return t[ni];
	}
	LL mid = (t[ni].l + t[ni].r) >> 1;
	if (r <= mid) return query(ni << 1, l, r);
	else if (mid < l) return query(ni << 1 | 1, l, r);
	else {
		Tree res, t1, t2;
		t1 = query(ni << 1, l, r);
		t2 = query(ni << 1 | 1, l, r);
		res.sum = t1.sum + t2.sum;
		res.pre = max(t1.pre, t1.sum + t2.pre);
		res.suf = max(t2.suf, t1.suf + t2.sum);
		res.mx = max({t1.mx, t2.mx, t1.suf + t2.pre});
		return res;
	} 
}

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

int n, m;
int a[500050];

struct Tree {
	int l, r;
	LL sum, pre, suf, mx;
}t[2000050];

void pu(int ni) {
	t[ni].sum = t[ni << 1].sum + t[ni << 1 | 1].sum;
	t[ni].pre = max(t[ni << 1].pre, t[ni << 1].sum + t[ni << 1 | 1].pre);
	t[ni].suf = max(t[ni << 1 | 1].suf, t[ni << 1].suf + t[ni << 1 | 1].sum);
	t[ni].mx = max({t[ni << 1].mx, t[ni << 1 | 1].mx, t[ni << 1].suf + t[ni << 1 | 1].pre});
}

void build_tree(int ni, int l, int r) {
	t[ni].l = l; t[ni].r = r;
	if (l == r) {
		t[ni].sum = t[ni].pre = t[ni].suf = t[ni].mx = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build_tree(ni << 1, l, mid);
	build_tree(ni << 1 | 1, mid + 1, r);
	pu(ni);
}

void fix(int ni, int pos, int x) {
	if (t[ni].l == t[ni].r and t[ni].l == pos) {
		t[ni].sum = t[ni].pre = t[ni].suf = t[ni].mx = x;
		return;
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	if (pos <= mid) fix(ni << 1, pos, x);
	else fix(ni << 1 | 1, pos, x);
	pu(ni); 
}

Tree query(int ni, int l, int r) {
	if (l <= t[ni].l and t[ni].r <= r) {
		return t[ni];
	}
	LL mid = (t[ni].l + t[ni].r) >> 1;
	if (r <= mid) return query(ni << 1, l, r);
	else if (mid < l) return query(ni << 1 | 1, l, r);
	else {
		Tree res, t1, t2;
		t1 = query(ni << 1, l, r);
		t2 = query(ni << 1 | 1, l, r);
		res.sum = t1.sum + t2.sum;
		res.pre = max(t1.pre, t1.sum + t2.pre);
		res.suf = max(t2.suf, t1.suf + t2.sum);
		res.mx = max({t1.mx, t2.mx, t1.suf + t2.pre});
		return res;
	} 
}

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
	}
	build_tree(1, 1, n);
	for (int i = 1; i <= m; ++i) {
		int op, x, y;
		cin >> op >> x >> y;
		if (op == 1) {
			if (x > y) swap(x, y);
			cout << query(1, x, y).mx << "\n";
		}
		else {
			fix(1, x, y);
		}
	}
	return 0;
}
举报

相关推荐

0 条评论