UVALive 3938 Ray, Pass me the dishes! (动态最大连续和)
题意:求一个动态区间的最大连续和。
静态版本的O(n)算法显示不适用了,但是可以用线段树分治,因为一个连续和要在两边的区间,要么跨越两边,对于一个结点维护最大前缀和,后缀和,子区间连续和。
题目要求输出区间,所以还要保存连续和最大的区间,以及前缀和,后缀和的位置。为了维护最大前缀和以及后缀和还需要一个区间和。
写的时候稍微麻烦一点,更新写成一个函数会方便很多。还好一遍过了。。。
#include<bits/stdc++.h> using namespace std; const int maxn = 5e5+2; typedef long long ll; struct Seg { ll pre,suf,sub,sum; int l,r,pr,sl; }tr[maxn<<2]; #define lid (id<<1) #define rid (id<<1|1) int n,m,a[maxn]; int ql,qr; void updata(Seg&u,Seg&v1,Seg&v2) { if(v1.pre >= v1.sum+v2.pre){//y u.pr = v1.pr; u.pre = v1.pre; }else { u.pr = v2.pr; u.pre = v1.sum+v2.pre; } if(v2.suf <= v2.sum+v1.suf){//x u.sl = v1.sl; u.suf = v2.sum+v1.suf; }else { u.sl = v2.sl; u.suf = v2.suf; } if(v1.sub >= v2.sub){ u.l = v1.l; u.r = v1.r; u.sub = v1.sub; }else { u.l = v2.l; u.r = v2.r; u.sub = v2.sub; } if(u.sub < v1.suf+v2.pre || (u.sub == v1.suf+v2.pre && (u.l>v1.sl ||(u.l == v1.sl && u.r > v2.pr) ) ) ){ u.sub = v1.suf+v2.pre; u.l = v1.sl; u.r = v2.pr; } u.sum = v1.sum + v2.sum; } void build(int l = 1,int r = n,int id = 1) {
if(l == r) {
Seg &u = tr[id]; u.pre = u.suf = u.sub = u.sum = a[l]; u.l = u.r = u.pr = u.sl = l; return; } int mid = (l+r)>>1, lc = lid, rc = rid; build(l,mid,lc); build(mid+1,r,rc); updata(tr[id],tr[lc],tr[rc]); } Seg query(int l = 1,int r = n, int id = 1) { if(ql<=l&&r<=qr) { return tr[id]; } int mid = (l+r)>>1, lc = lid, rc = rid; Seg ret; if(ql<=mid && mid<qr){ Seg L = query(l,mid,lc), R = query(mid+1,r,rc); updata(ret,L,R); return ret; } if(qr <= mid) { return query(l,mid,lc); } return query(mid+1,r,rc); } int main() { //freopen("in.txt","r",stdin); int kas = 0; while(~scanf("%d%d",&n,&m)){ for(int i = 1; i <= n; i++) scanf("%d",a+i); build(); printf("Case %d:\n",++kas); while(m--){ int x,y; scanf("%d%d",&x,&y); ql = x,qr = y; Seg ans = query(); printf("%d %d\n",ans.l,ans.r); } } return 0; }