题解 P5693【EI 的第六分块】

$\text{Link}$

“$\text{KTT}$”学习笔记。

不含复杂度分析。理解上参考学习了 @optimize_2这篇题解

题意

区间加正数,区间最大子段和。

$n,q\le4\times10^5$。

思路

线段树求区间最大子段和需要维护的有区间和 $sum$,最大前缀和 $lmax$,最大后缀和 $rmax$,最大子段和 $totmax$ 四个参数。

考虑修改操作对这几个值的影响。

对区间加上 $k$,如果选择的区间没有发生改变,则这个答案会变大 $lk$,其中 $l$ 为选择的区间长度。

我们考虑将上述四个值换用一次函数表示,使其表示为 $ls+b$,如果没有改变选择的区间则 $s$ 每次增加 $k$。

为了判断选择的区间是否改变,除了加法标记,我们需要额外维护一个 $x$,表示如果 $s\ge x$,则 $lmax,rmax,totmax$ 选择的区间至少有一个一定会改变。

考虑 pushup:$+$ 操作为一次函数相加,$\max$ 操作为一次函数在 $s=0$ 处的比较。

  • $sum=l.sum+r.sum$
  • $lmax=\max(l.lmax,l.sum+r.lmax)$
  • $rmax=\max(r.rmax,l.rmax+r.sum)$
  • $totmax=\max(l.totmax,r.totmax,l.rmax+r.lmax)$
  • $x$ 在 $\min(l.x,r.x)$ 的基础上,对 $lmax,rmax$ 的两种方案、$totmax$ 的三种方案的一次函数交点位置取 $\min$(小于 $0$ 或无交点变为 INF

在实现中,我们每次把 $x$ 减小 $k$,如果 $x$ 在一次减小后小于 $0$,则 dfs 这个子树,如果搜到的子树 $x$ 没有变为 $0$ 就返回。

通过复杂的势能分析,我们会得到复杂度为 $O((n+m)\log^3n+q\log n)$,其中 $m$ 为修改次数,$q$ 为查询次数。

代码实现:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=4e5+10;
const ll INF=1e15;
int n,Q,p[N];
#define ls (rt<<1)
#define rs (rt<<1|1)
struct Func{
    int k;
    ll b;
    inline friend Func operator+(const Func &a,const Func &b){
        return (Func){a.k+b.k,a.b+b.b};
    }
    inline void add(ll v){ b+=k*v; }
};
inline pair<Func,ll> max(Func a,Func b){
    if(a.k<b.k||a.k==b.k&&a.b<b.b) swap(a,b);
    if(a.b>=b.b) return make_pair(a,INF);
    return make_pair(b,(b.b-a.b)/(a.k-b.k));
}
struct node{
    Func lmax,rmax,totmax,sum;
    ll x;
    inline friend node operator+(const node &a,const node &b){
        node t;
        pair<Func,ll> tmp;
        t.x=min(a.x,b.x);
        tmp=max(a.lmax,b.lmax+a.sum);
        t.lmax=tmp.first,t.x=min(t.x,tmp.second);
        tmp=max(b.rmax,a.rmax+b.sum);
        t.rmax=tmp.first,t.x=min(t.x,tmp.second);
        tmp=max(a.totmax,b.totmax);
        t.x=min(t.x,tmp.second);
        tmp=max(tmp.first,a.rmax+b.lmax);
        t.totmax=tmp.first,t.x=min(t.x,tmp.second);
        t.sum=a.sum+b.sum;
        return t;
    }
};
struct KTT{
    node a[N<<2];
    ll tag[N<<2];
    inline void pushup(int rt){
        a[rt]=a[ls]+a[rs];
    }
    inline void push(int rt,ll v){
        tag[rt]+=v;
        a[rt].x-=v;
        a[rt].lmax.add(v);
        a[rt].rmax.add(v);
        a[rt].sum.add(v);
        a[rt].totmax.add(v);
    }
    inline void pushdown(int rt){
        if(tag[rt]){
            ll bas=tag[rt];
            tag[rt]=0;
            push(ls,bas);
            push(rs,bas);
        }
    }
    inline void build(int rt,int l,int r){
        if(l==r){
            Func q={1,p[l]};
            a[rt]=(node){q,q,q,q,INF};
            return ;
        }
        int mid=(l+r)>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
        pushup(rt);
    }
    inline void defeat(int rt,int l,int r,ll v){
        if(v>a[rt].x){
            int mid=l+r>>1;
            ll t=tag[rt]+v;
            tag[rt]=0;
            defeat(ls,l,mid,t);
            defeat(rs,mid+1,r,t);
            pushup(rt);
        }else{
            push(rt,v);
        }
    }
    inline void update(int rt,int l,int r,int L,int R,int k){
        if(L<=l&&r<=R){
            defeat(rt,l,r,k);
            return ;
        }
        pushdown(rt);
        int mid=l+r>>1;
        if(L<=mid) update(ls,l,mid,L,R,k);
        if(R>mid) update(rs,mid+1,r,L,R,k);
        pushup(rt);
    }
    inline node query(int rt,int l,int r,int L,int R){
        if(L<=l&&r<=R){
            return a[rt];
        }
        pushdown(rt);
        int mid=l+r>>1;
        if(R<=mid) return query(ls,l,mid,L,R);
        if(L>mid) return query(rs,mid+1,r,L,R);
        return query(ls,l,mid,L,mid)+query(rs,mid+1,r,mid+1,R);
    }
}t;
int main(){
    n=read(),Q=read();
    for(int i=1;i<=n;i++){
        p[i]=read();
    }
    t.build(1,1,n);
    while(Q--){
        int opt=read();
        switch(opt){
            case 1:{
                int l=read(),r=read(),v=read();
                t.update(1,1,n,l,r,v);
                break;
            }
            case 2:{
                int l=read(),r=read();
                write(max(0ll,t.query(1,1,n,l,r).totmax.b)),putc('\n');
                break;
            }
        }
    }
    flush();
    return 0;
} 

再见 qwq~

posted @ 2023-02-08 22:01  ffffyc  阅读(39)  评论(0)    收藏  举报  来源