KTT 区间增量最大子段和

KTT 区间增量最大子段和

例题:P5693 EI 的第六分块。

支持单点加的最大子段和是这样的:线段树上每个区间维护区间和、前缀最大值、后缀最大值、最大子段和。

变成区间加,如果区间加完 \(v(v>0)\) 以后作为最大子段和的区间没有改变,则增量为 \(len\times v\)。这启示我们保留元素的区间长度信息,以及知道什么时候元素的区间会改变。

我们把上述四种信息改成一次函数 \(kx+b\),其中 \(k\) 为区间长度,\(b\) 为原来的值。这样进行加法的时候执行 \(b:=b+k\times v\)。则取最大值就是根据 \(b\) 取最大值。

接下来要知道元素的区间什么时候改变,我们在合并多个一次函数的时候(区间和、前缀最大值、后缀最大值分别有两个,与最大子段和的三个),求出最小交点,记作 \(lim\),同时其还要对左右儿子的 \(lim\)\(\min\)。则在区间加 \(v\) 时,将 \(lim:=lim-v\),若 \(lim<0\) 则说明子树内有元素的区间将要改变,此时我们更新子树,重新计算所有 \(lim<0\) 的节点。

还有一个小疑问:我们在下传加法标记的时候是不用关心 \(lim\) 的,因为此前做加法的时候 \(lim\) 是否带来更新就已经考虑了,所以现在 \(lim\) 是不会带来更新的。

复杂度是 \(O((n+m)\log ^3n+q\log n)\),但实测常数很小,例题 \(4\times 10^5\) 只跑到了 1s 左右。


实现细节,我们用封装一个一次函数类,只需要支持对位相加还有加 \(v\) 操作。

再封装每个节点,其中要包含 \(lim\) 信息,以及节点的合并操作;懒标记独立于这个类。这样方便查询时合并。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ls (x<<1)
#define rs (ls|1)
#define mid ((l+r)>>1)
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N=4e5+5;
const ll inf=1e18;
int n,Q,A[N];
template<typename T> void ckmin(T &x,T y) {if(y<x) x=y;}
struct line {
    ll k,b;
    line operator + (line o) {
        return line {k+o.k, b+o.b};
    }
    void add(ll v) {
        b+=k*v;
    }
};
void ckmax(line &res,ll &lim,line a,line b) {
    if(a.k>b.k || a.k==b.k && a.b>b.b) swap(a,b);
    if(a.b<=b.b) res=b;
    else res=a, ckmin(lim,(a.b-b.b)/(b.k-a.k));
}
struct arr {
    line sm,p,q,s; // 区间和、前缀最大值、后缀最大值、子段和
    ll lim;
    arr operator + (arr o) {
        arr nw;
        nw.sm=sm+o.sm;
        nw.lim=min(lim,o.lim);
        ckmax(nw.p,nw.lim,p,sm+o.p);
        ckmax(nw.q,nw.lim,o.q,o.sm+q);
        ckmax(nw.s,nw.lim,s,o.s);
        ckmax(nw.s,nw.lim,nw.s,q+o.p);
        return nw;
    }
    void add(ll v) {
        lim-=v;
        p.add(v);
        q.add(v);
        s.add(v);
        sm.add(v);
    }
}a[N*4];
ll tag[N*4];
void build(int x,int l,int r) {
    if(l==r) {
        auto tmp=line{1,A[l]};
        a[x]={tmp,tmp,tmp,tmp,inf};
        return;
    }
    build(lson),build(rson);
    a[x]=a[ls]+a[rs];
}
void add(int x,ll v) {
    tag[x]+=v;
    a[x].add(v);
}
void dn(int x) {
    if(tag[x]) {
        add(ls,tag[x]);
        add(rs,tag[x]);
        tag[x]=0;
    }
}
void dg(int x,int l,int r,ll v) {
    if(v>a[x].lim) {
        dg(lson,v+tag[x]);
        dg(rson,v+tag[x]);
        tag[x]=0;
        a[x]=a[ls]+a[rs];
    }
    else add(x,v);
}
void upd(int x,int l,int r,int L,int R,ll v) {
    if(L<=l && r<=R) return dg(x,l,r,v);
    dn(x);
    if(L<=mid) upd(lson,L,R,v);
    if(R>mid) upd(rson,L,R,v);
    a[x]=a[ls]+a[rs];
}
arr qry(int x,int l,int r,int L,int R) {
    if(L<=l && r<=R) return a[x];
    dn(x);
    if(R<=mid) return qry(lson,L,R);
    if(L>mid) return qry(rson,L,R);
    return qry(lson,L,R)+qry(rson,L,R); 
}
int main() {
    ios::sync_with_stdio(0); cin.tie(0);
    cin>>n>>Q;
    for(int i=1;i<=n;++i) {
        cin>>A[i];
    }
    build(1,1,n);
    for(int i=1;i<=Q;++i) {
        int opt; cin>>opt;
        if(opt==1) {
            int l,r; ll v;
            cin>>l>>r>>v;
            upd(1,1,n,l,r,v);
        }
        else {
            int l,r; cin>>l>>r;
            cout<<max(0ll,qry(1,1,n,l,r).s.b)<<'\n';
        }
    }
}
posted @ 2026-08-12 21:33  dengchengyu  阅读(5)  评论(0)    收藏  举报