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: ",++kas); while(m--){ int x,y; scanf("%d%d",&x,&y); ql = x,qr = y; Seg ans = query(); printf("%d %d ",ans.l,ans.r); } } return 0; }
原文地址:https://www.cnblogs.com/jerryRey/p/4792519.html