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';
}
}
}

浙公网安备 33010602011771号