【算法】线段树
【题解】
学自:https://vijos.org/p/1083/solution(wang_yanheng的回答)
回溯时维护一段区间的以下域:
- sumL:从左端点起连续区间的最大和
- sumR:从右端点起连续区间的最大和
- sum:整段区间的和
- subSum:最大子区间和
以上域在叶子节点中的值,都等于节点代表公园的评分
设当前节点为 x,左孩子为 l,右孩子为 r。则:
x.sumL = MAX(l.sumL, l.sum + r.sumL);
x.sumR = MAX(r.sumR, r.sum + l.sumR);
x.sum = l.sum + r.sum;
x.subSum = MAX(l.subSum, r.subSum, l.sumR + r.sumL);
以上计算过程可以写成函数,参数结构体,返回结构体。
查询时要返回结构体。
#include<cstdio> #include<algorithm> using namespace std; const int inf=0x3f3f3f3f,maxn=500010; struct cyc{int lsum,rsum,sum,subsum;}t[maxn*3]; int a[maxn],L[maxn*3],R[maxn*3],n,m; cyc calc(cyc a,cyc b) { cyc c; c.lsum=max(a.lsum,a.sum+b.lsum); c.rsum=max(b.rsum,b.sum+a.rsum); c.sum=a.sum+b.sum; c.subsum=max(a.subsum,max(b.subsum,a.rsum+b.lsum)); return c; } void build(int k,int l,int r) { L[k]=l;R[k]=r; if(l==r)t[k]={a[l],a[l],a[l],a[l]}; else { int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); t[k]=calc(t[k<<1],t[k<<1|1]); } } void update(int k,int x,int num) { int left=L[k],right=R[k]; if(left==right)t[k]={num,num,num,num}; else { int mid=(left+right)>>1; if(x<=mid)update(k<<1,x,num); else update(k<<1|1,x,num); t[k]=calc(t[k<<1],t[k<<1|1]); } } cyc ask(int k,int l,int r) { int left=L[k],right=R[k]; if(l<=left&&right<=r)return t[k]; else { int mid=(left+right)>>1; if(l>mid)return ask(k<<1|1,l,r); if(r<=mid)return ask(k<<1,l,r); return calc(ask(k<<1,l,r),ask(k<<1|1,l,r)); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); build(1,1,n); for(int i=1;i<=m;i++) { int k,b,c; scanf("%d%d%d",&k,&b,&c); if(k==1) { if(b>c)swap(b,c); printf("%d ",ask(1,b,c).subsum); } else update(1,b,c); } }