我们知道,求一段序列的最大子段和是O(n)的,但是这样是显然会超时的。
我们需要一个数据结构来支持修改和计算的操作,对于这种修改一个而查询区间的问题,考虑使用线段树。
在线段树中,除了左端点,右端点,左儿子指针,右儿子指针之外,新开4个域——max,maxl,maxr,sum,其中sum为该区间的和,max为该区间上的最大子段和,maxl为必须包含左端点的最大子段和,maxr为必须包含右端点的最大子段和。
可以用线段树来统计了注意求得的最大子段和中至少包含1个元素,所以出现了样例那样的输出负值。
修改时:
1、若左儿子的maxr和右儿子的maxl都为负,就从中取较大的为该节点的max(防止一个都不取),反之取二者中正的(都正就都取)。
2、将该节点的max用左右儿子的max更新。
3、该节点的maxl为左儿子的maxl与左儿子sum和右儿子maxl和的最大值。
4、该节点的maxr为右儿子的maxr与右儿子sum和左儿子maxr和的最大值。
5、该节点的sum为左右儿子的sum和。
查询时:
1、如果查询区间覆盖这一节点,将该节点信息返回。
2、如果只与一个儿子有交集,就返回在那个儿子中查找到的信息。
3、如果与两个儿子都有交集,就先分别计算出两个儿子的信息,然后按修改的方式将两个信息合并,然后返回。
4、最后返回的max值即为答案。
#include<cstdio> #include<iostream> using namespace std; struct node{ int l,r,mm,lm,rm,v; }tr[2000010]; void update(int k) { tr[k].v=tr[k*2].v+tr[k*2+1].v; tr[k].lm=max(tr[k*2].lm,tr[k*2].v+tr[k*2+1].lm); tr[k].rm=max(tr[k*2+1].rm,tr[k*2+1].v+tr[k*2].rm); tr[k].mm=max(tr[k*2].mm,max(tr[k*2+1].mm,tr[2*k].rm+tr[k*2+1].lm)); } void build(int l,int r,int k) { tr[k].l=l; tr[k].r=r; if(l==r) { cin>>tr[k].v; tr[k].lm=tr[k].v; tr[k].rm=tr[k].v; tr[k].mm=tr[k].v; return; } int mid=(l+r)/2; build(l,mid,k*2); build(mid+1,r,k*2+1); update(k);//回溯时求该区间最大值。 } void change(int pos,int num,int k){ if(tr[k].l==pos && tr[k].r==pos){ tr[k].v=num; tr[k].lm=num,tr[k].rm=num,tr[k].mm=num; return; } int mid=(tr[k].l+tr[k].r)>>1; if(pos<=mid) change(pos,num,k*2); else change(pos,num,k*2+1);//向下二分搜索 ; update(k);//回溯时改变区间最大值; } node sear(int l,int r,int k)//由于和update有一样操作直接找结构体 { node left1,right1; int f1=0,f2=0; if(tr[k].l>=l && tr[k].r<=r) return tr[k]; int mid=(tr[k].l+tr[k].r)/2; if(l<=mid) left1 = sear(l,r,k*2),f1=1;//如果区间包含中线左边 。 if(r>mid) right1 = sear(l,r,k*2+1),f2=1;//如果区间包含中线右边。 if(f2 && f1)//如果区间同时包含中线左右边则需进行update时一样的操作。 { node m; m.l = left1.l, m.r = right1.r; m.v = left1.v+right1.v; m.lm = max(left1.lm, left1.v+right1.lm); m.rm = max(right1.rm, right1.v+left1.rm); m.mm = max(left1.mm, max(right1.mm, left1.rm + right1.lm)) ; return m; } if(f1)//只包含左边; return left1; if(f2)//只包含右边; return right1; } int main() { int n,m,type,le,ri; cin>>n>>m; build(1,n,1); for(int i=1;i<=m;i++) { cin>>type>>le>>ri; if(type==1 && le>ri) swap(le,ri); if(type==1) { node ans=sear(le,ri,1); cout<<ans.mm<<endl; } else change(le,ri,1); } return 0; }