问题:对于一颗线段树,每次询问[L,R],求此区间中任意一段连续序列,此连续序列和最大。
解法:每个节点维护4个值:
max:此区间内的最大连续和
sum:该节点以下的节点值得总和
lmax:此区间的从左端开始的最大连续和
rmax:此区间的从右端开始的最大连续和
合并区间时,该区间的最大连续和为:max(左子节点的最大连续和,右子节点的最大连续和,左子节点的最大右连续和+右子节点的最大左连续和)
查询时返回一个整节点。因为每次要查询左子节点和右子节点,并且要比较它们的右连续最大和和左连续最大和,所以需要返回整个节点以便求值。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define N 200007 struct node { int maxi,lmaxi,rmaxi,sum; }tree[4*N]; void pushup(int rt) { tree[rt].sum = tree[2*rt].sum + tree[2*rt+1].sum; tree[rt].maxi = max(tree[2*rt].maxi,max(tree[2*rt+1].maxi,tree[2*rt].rmaxi+tree[2*rt+1].lmaxi)); tree[rt].lmaxi = max(tree[2*rt].lmaxi,tree[2*rt].sum + tree[2*rt+1].lmaxi); tree[rt].rmaxi = max(tree[2*rt+1].rmaxi,tree[2*rt+1].sum + tree[2*rt].rmaxi); } void build(int l,int r,int rt) { if(l == r) { scanf("%d",&tree[rt].sum); tree[rt].maxi = tree[rt].lmaxi = tree[rt].rmaxi = tree[rt].sum; return; } int mid = (l+r)/2; build(l,mid,2*rt); build(mid+1,r,2*rt+1); pushup(rt); } void update(int l,int r,int pos,int val,int rt) { if(l == r) { tree[rt].maxi = tree[rt].lmaxi = tree[rt].rmaxi = tree[rt].sum = val; return; } int mid = (l+r)/2; if(pos <= mid) update(l,mid,pos,val,2*rt); else update(mid+1,r,pos,val,2*rt+1); pushup(rt); } node query(int l,int r,int aa,int bb,int rt) { if(aa <= l && bb >= r) return tree[rt]; int mid = (l+r)/2; node ka,kb,res; int flag1 = 0; int flag2 = 0; if(aa <= mid) { ka = query(l,mid,aa,bb,2*rt); flag1 = 1; } if(bb > mid) { kb = query(mid+1,r,aa,bb,2*rt+1); flag2 = 1; } if(flag1 && flag2) { res.sum = ka.sum + kb.sum; res.lmaxi = max(ka.lmaxi,ka.sum+kb.lmaxi); res.rmaxi = max(kb.rmaxi,kb.sum+ka.rmaxi); res.maxi = max(ka.rmaxi+kb.lmaxi,max(ka.maxi,kb.maxi)); } else { if(flag1) //left res = ka; else res = kb; } return res; } int main() { int n,m,op,aa,bb; scanf("%d%d",&n,&m); build(1,n,1); while(m--) { scanf("%d%d%d",&op,&aa,&bb); if(!op) { node res = query(1,n,aa,bb,1); printf("%d ",res.maxi); } else update(1,n,aa,bb,1); } return 0; }