博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

SPOJ GSS1 静态区间最大子段和

题目大意

给定一个序列,支持区间最大子段和,\(n \leq 50000\)

思路

一个序列的最大字段和可能出现在左半侧最大子段和,右半侧最大子段和和跨越两边的最大子段和。

维护四个标记:mxsum,mxpre,mxsuf,sum,线段树操作即可。

有一个细节,查询的时候不能直接用左右孩子的最大后缀/前缀,因为查询的区间可能小于最大子段和出现的区间。

两种解决办法:

  • 记录最大子段和出现的位置,维护的时候判断边界
  • 将尽可能多的信息传递上来,用于计算。

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 10;
struct node {
	int l,r;
	int mxsum;
	int mxsuf;
	int mxpre;
	int sum;
};

int a[N];
struct segment_tree {
	node t[N << 2];

	void update(int rt) {
		int ch = rt << 1;
		t[rt].sum =  t[ch].sum + t[ch + 1].sum;
		t[rt].mxsum = max(max(t[ch].mxsum,t[ch + 1].mxsum),t[ch].mxsuf + t[ch + 1].mxpre);
		t[rt].mxpre = max(t[ch].mxpre,t[ch].sum + t[ch + 1].mxpre);
		t[rt].mxsuf = max(t[ch + 1].mxsuf,t[ch + 1].sum + t[ch].mxsuf);
	}

	void build(int l,int r,int rt) {
		t[rt].l = l;
		t[rt].r = r;
		if(l == r) {
			t[rt].sum = t[rt].mxsuf = t[rt].mxpre = t[rt].mxsum = a[l];
			return;
		}
		int mid = (l + r) >> 1;
		int ch = rt << 1;
		build(l,mid,ch);
		build(mid + 1,r,ch + 1);
		update(rt);
	}

	node query (int l,int r,int rt) {
		if(l == t[rt].l and r == t[rt].r) {
			return t[rt];
		}
		int mid = (t[rt].l + t[rt].r) >> 1;
		int ch = rt << 1;
		if(r <= mid) {
			return query(l,r,ch);
		}
		else if(l > mid) {
			return query(l,r,ch + 1);
		}else {
			node ret1 = query(l,mid,ch);
			node ret2 = query(mid + 1,r,ch + 1);
			node res;
			res.sum = ret1.sum + ret2.sum;
			res.mxsum = max(max(ret1.mxsum,ret2.mxsum),ret1.mxsuf + ret2.mxpre);
			res.mxpre = max(ret1.mxpre,ret1.sum + ret2.mxpre);
			res.mxsuf = max(ret2.mxsuf,ret2.sum + ret1.mxsuf);
			return res;
		}
	}
} ST;

int n,q;

int main () {
	cin >> n;
	for(int i = 1;i <= n; i ++) {
		cin >> a[i];
	}
	ST.build(1,n,1);
	cin >> q;
	while(q --) {
		int l,r;
		cin >> l >> r;
		cout << ST.query(l,r,1).mxsum << endl;
	}
	return 0;
}

posted @ 2022-02-12 10:08  Allorkiya  阅读(86)  评论(0编辑  收藏  举报