poj2750(线段树+复杂区间合并)
Potted Flower
思路:我们将题目简单化,假设我们要求的是序列的最大连续子段和,且可以包括所有数。
我们的线段树需要维护这段区间的最大前缀和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;
}