3981 动态最大子段和
题目还是简单一点好...
有n个数,a[1]到a[n]。
接下来q次查询,每次动态指定两个数l,r,求a[l]到a[r]的最大子段和。
子段的意思是连续非空区间。
第一行一个数n。
第二行n个数a[1]~a[n]。
第三行一个数q。
以下q行每行两个数l和r。
q行,每行一个数,表示a[l]到a[r]的最大子段和。
7
2 3 -233 233 -23 -2 233
4
1 7
5 6
2 5
2 3
441
-2
233
3
对于50%的数据,q*n<=10000000。
对于100%的数据,1<=n<=200000,1<=q<=200000。
a[1]~a[n]在int范围内,但是答案可能超出int范围。
数据保证1<=l<=r<=n。
空间128M,时间1s。
线段树求GSS模板题
一、一段长的区间的 GSS 有三种情况:
>1 完全在左子区间
>2 完全在右子区间
>3 横跨左右区间
二、需维护的信息:
max 区间GSS ——用来更新情况1、2
max_l 区间左端点开始的GSS——用来更新情况3
max_r 区间右端点开始的GSS——用来更新情况3
sum 区间和——用来更新max_l,max_r
三、建树
1、初始化:区间需维护的信息最初都赋为输入值
2、合并区间信息
max:3中情况中的最大值
max_l:左区间的max_l, 左区间的sum+右区间max_l 取大
max_r 同理
四、查询
情况1、2很简单
情况3的合并与上面的合并区间信息同理
#include<cstdio> #include<algorithm> #include<iostream> using namespace std; int n,m,opl,opr,cnt; struct node { int l,r; long long max,max_l,max_r,sum; }e[4000001]; void query(int k,long long & ans,long long & ans_l,long long & ans_r) { if(e[k].l>=opl&&e[k].r<=opr) { ans=e[k].max; ans_l=e[k].max_l; ans_r=e[k].max_r; return; } int mid=e[k].l+e[k].r>>1; if(opr<=mid) query(k<<1,ans,ans_l,ans_r); else if(opl>mid) query((k<<1)+1,ans,ans_l,ans_r); else { long long lch_max=0,lch_max_r=0,rch_max=0,rch_max_l=0,lch_max_l=0,rch_max_r=0; query(k<<1,lch_max,lch_max_l,lch_max_r); query((k<<1)+1,rch_max,rch_max_l,rch_max_r); ans=max(lch_max,rch_max); ans=max(ans,lch_max_r+rch_max_l); ans_l=max(lch_max_l,e[k<<1].sum+rch_max_l); ans_r=max(rch_max_r,e[(k<<1)+1].sum+lch_max_r); } } void unionn(int k) { e[k].max=max(max(e[k<<1].max,e[(k<<1)+1].max),e[k<<1].max_r+e[(k<<1)+1].max_l); e[k].max_l=max(e[k<<1].max_l,e[k<<1].sum+e[(k<<1)+1].max_l); e[k].max_r=max(e[(k<<1)+1].max_r,e[(k<<1)+1].sum+e[k<<1].max_r); e[k].sum=e[k<<1].sum+e[(k<<1)+1].sum; } void build(int k,int l,int r) { e[k].l=l,e[k].r=r; if(l==r) { cin>>e[k].max; e[k].max_l=e[k].max_r=e[k].sum=e[k].max; return; } int mid=l+r>>1; build(k<<1,l,mid); build((k<<1)+1,mid+1,r); unionn(k); } int main() { scanf("%d",&n); build(1,1,n); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d%d",&opl,&opr); long long ans=0,ans_l=0,ans_r=0; query(1,ans,ans_l,ans_r); printf("%lld\n",ans); } }
自己做的2个错误:
1、max_l的更新 : if 左区间的GSS是本身,max_l=max(左区间的max_l,左区间的sum+右区间的max_l)
else max_l=左区间的max_l
错因:例:左区间:5,8,-1,-1 右区间 1,1,1,1
错误方法的GSS=13 正确的GSS=15
2、在查询时ans,ans_l,ans_r的更新同max,max_l,max_r
因为max,max_l,max_r更新用到的左区间、右区间信息可以直接拿来用
而ans,ans_l,ans_r 更新用到的左区间、右区间信息不能直接拿来用,感觉比较棘手,实际只要在递归回溯时更新即可