poj2750(线段树+复杂区间合并)

Potted Flower

POJ - 2750

思路:我们将题目简单化,假设我们要求的是序列的最大连续子段和,且可以包括所有数。

我们的线段树需要维护这段区间的最大前缀和pre,最大后缀和suf,区间和sum,区间连续最大和mx

那么难点就在于如何由子节点更新父节点。

我们可以知道,tr[p].sum = tr[lc].sum + tr[rc].sum //p为父节点,lc为左孩子,rc为右孩子

tr[p].pre = max(tr[lc].pre,tr[lc].sum+tr[rc].pre) //左孩子的pre与左孩子的区间和加右孩子的pre取最大值

同理:tr[p].suf = max(tr[rc].suf,tr[rc].sum+tr[lc].suf)

那么如何更新区间最大和mx呢?

我们有:tr[p].mx = max(tr[lc].mx , tr[rc].mx ) //我们在左右孩子的mx中取最大值

但如果最大区间和在中间呢?

可以这样写

tr[p].mx = max(tr[p].mx , tr[lc].suf+ tr[rc].pre) //我们维护pre和suf就是为了解决中间的情况

我们已经解决了序列的最大连续字段和了,但是题目要求的是环,如何求?

假设我们知道了这段序列的连续最小和mi (跟mx的意思反一下,求得是最小)

那么我们用总的区间和sum减去 mi 的结果再与mx取最大就是结果

例:1 -3 -2 3 5 这个例子中mx为8 (3+5),mi 为-5 (-3 + (- 2) ) , sum为4 结果为 max( 8, 4 - (-5)) = 9,(因为是环,所以为 1 + 5 + 3)

求区间连续最小和mi与求最大和mx是一样的,最小前缀和pre_1,最小后缀和suf_1

#define _CRT_SECURE_NO_WARNINGS 1
#include<algorithm>
#include<fstream>
#include<iostream>
#include<cstdio>
#include<deque>
#include<string>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
//#include<unordered_map>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 310000
#define N 2100100
#define endl '\n'
#define exp 1e-8
#define lc p << 1
#define rc p << 1|1
#define lowbit(x) ((x)&-(x))
const double pi = acos(-1.0);
typedef long long LL;
typedef unsigned long long ULL;
inline LL read() {
	LL x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 1) + (x << 3) + (ch ^ 48);
		ch = getchar();
	}
	return x * f;
}
struct tree
{
	int l, r, pre, suf,mx,sum,pre_1,suf_1,mi;
}tr[4*N];
int arr[N],n,m;
void pushup(int p)  //核心代码
{
	tr[p].sum = tr[lc].sum + tr[rc].sum;

	tr[p].pre = max(tr[lc].pre, tr[lc].sum + tr[rc].pre);  //求最大
	tr[p].suf = max(tr[rc].suf, tr[lc].suf + tr[rc].sum);
	tr[p].mx = max(tr[lc].mx, tr[rc].mx);
	tr[p].mx = max(tr[p].mx, tr[lc].suf + tr[rc].pre);
        //tr[p].mx = max(tr[p].mx, tr[p].sum) 可以不写这行代码,想想为什么

	tr[p].pre_1 = min(tr[lc].pre_1, tr[lc].sum + tr[rc].pre_1);  //求最小
	tr[p].suf_1 = min(tr[rc].suf_1, tr[rc].sum + tr[lc].suf_1);
	tr[p].mi = min(tr[lc].mi, tr[rc].mi );
	tr[p].mi = min(tr[p].mi, tr[lc].suf_1 + tr[rc].pre_1);
}
void build(int p, int l, int r)
{
	tr[p].l = l, tr[p].r = r;
	if (l == r) {tr[p].pre = tr[p].suf = tr[p].mx = tr[p].sum = tr[p].pre_1 = tr[p].suf_1 = tr[p].mi = arr[l]; return; }
	int m = l + r >> 1;
	build(lc, l, m);
	build(rc, m + 1, r);
	pushup(p);
}
void update(int p, int x, int k) //单点修改
{
	if (tr[p].l == tr[p].r)
	{
		tr[p].pre = tr[p].suf = tr[p].mx = tr[p].sum = tr[p].pre_1 = tr[p].suf_1 = tr[p].mi = k;
		return;
	}
	int m = tr[p].l + tr[p].r >> 1;
	if (x <= m)update(lc, x, k);
	else update(rc, x, k);
	pushup(p);
}
int main()
{
	n = read();
	for (int i = 1; i <= n; i++)
	{
		arr[i] = read();
	}
	build(1, 1, n);
	m = read();
	while (m--)
	{
		int a = read(), b = read();
		update(1, a, b);
		if (tr[1].mx == tr[1].sum)  //因为题目说不能连续n个,所以当mx == sum时,我们要减去最小的
			cout << tr[1].sum - tr[1].mi <<endl;
		else 
		    cout << max(tr[1].mx, (tr[1].sum - tr[1].mi)) <<endl; 
	}
	return 0;
}
posted @ 2023-04-15 18:10  魏老6  阅读(30)  评论(0编辑  收藏  举报