洛谷P4513 小白逛公园 (线段树)
这道题看起来像是线段树和最大子段和的结合,但这里求最大子段和不用dp,充分利用线段树递归的优势来处理。个人理解:线段树相当于把求整个区间的最大子段和的问题不断划分为很多个小问题,容易解决小问题,然后递归处理较大的问题(分治),所以这就可以用来解决。
在线段树中,除了左端点,右端点,新开4个域——ans,ml,mr,sum,其中sum为该区间的和,ans为该区间上的最大子段和,ml为必须包含左端点(以左端点为头)的最大子段和,mr为必须包含右端点(以右端点为尾)的最大子段和。
更新操作在up()中,应该容易看懂,需要思考的是查询操作,他返回的是一个结构体类型,该操作相当于针对某个询问的区间来更新结果,最后的query().ans也就是答案了
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=5e5+10; 5 int n,m; 6 struct node{ 7 int l,r; 8 ll ml,mr,sum,ans; 9 }t[N<<2]; 10 11 void up(int k){ 12 t[k].sum=t[k<<1].sum+t[k<<1|1].sum; 13 t[k].ml=max(t[k<<1].ml,t[k<<1].sum+t[k<<1|1].ml); 14 t[k].mr=max(t[k<<1|1].mr,t[k<<1|1].sum+t[k<<1].mr); 15 t[k].ans=max(max(t[k<<1].ans,t[k<<1|1].ans),t[k<<1].mr+t[k<<1|1].ml); 16 } 17 18 void build(int k,int l,int r){ 19 t[k].l=l,t[k].r=r; 20 if(l==r){ 21 scanf("%lld",&t[k].sum); 22 t[k].ml=t[k].mr=t[k].ans=t[k].sum; 23 return ; 24 } 25 int mid=(l+r)>>1; 26 build(k<<1,l,mid);build(k<<1|1,mid+1,r); 27 up(k); 28 } 29 30 node query(int k,int l,int r){ 31 if(t[k].l>=l && t[k].r<=r) return t[k]; 32 int mid=(t[k].l+t[k].r)>>1; 33 if(r<=mid) return query(k<<1,l,r); 34 else if(l>mid) return query(k<<1|1,l,r); 35 else{ 36 node t,a=query(k<<1,l,r),b=query(k<<1|1,l,r); 37 t.sum=a.sum+b.sum; 38 t.ml=max(a.ml,a.sum+b.ml); 39 t.mr=max(b.mr,b.sum+a.mr); 40 t.ans=max(max(a.ans,b.ans),a.mr+b.ml);//合并 41 return t; 42 } 43 } 44 45 void change(int k,int p,int x){//将p位置上的数改为x 46 if(t[k].l==t[k].r && t[k].l==p){ 47 t[k].ml=t[k].mr=t[k].ans=t[k].sum=x; 48 return ; 49 } 50 int mid=(t[k].l+t[k].r)>>1; 51 if(p<=mid) change(k<<1,p,x); 52 else change(k<<1|1,p,x); 53 up(k); 54 } 55 56 int main(){ 57 scanf("%d%d",&n,&m); 58 build(1,1,n); 59 while(m--){ 60 int opt,a,b; 61 scanf("%d%d%d",&opt,&a,&b); 62 if(opt==1){ 63 if(a>b) swap(a,b); 64 printf("%lld\n",query(1,a,b).ans); 65 } 66 else change(1,a,b); 67 } 68 }