2018.07.28 uoj#164. 【清华集训2015】V(线段树)

时间:2021-02-11 23:00:27

传送门

线段树好题。

要求支持的操作:

1.区间变成max(xi−a,0)" role="presentation" style="position: relative;">max(xi−a,0)max(xi−a,0)

2.区间加

3.区间覆盖

4.询问单点最值

5.询问单点历史最值

注:以下提到的标记都是向下传递的懒标记。

这题直接更新区间最大值貌似不好维护,因此我们需要更换思路,对于每个节点维护一个标记(a,b)" role="presentation" style="position: relative;">(a,b)(a,b),来表示在上一次修改这个区间之后当前区间的所有数都变成(xi+a,b)" role="presentation" style="position: relative;">(xi+a,b)(xi+a,b),这样的话,第一个操作变成了(−a,0)" role="presentation" style="position: relative;">(−a,0)(−a,0),第二个操作变成了(a,−inf)" role="presentation" style="position: relative;">(a,−inf)(a,−inf),第三个操作变成了(−inf,a)" role="presentation" style="position: relative;">(−inf,a)(−inf,a)。然后查询的时候只用查询单点的(a,b)" role="presentation" style="position: relative;">(a,b)(a,b),然后输出(xi+a,b)" role="presentation" style="position: relative;">(xi+a,b)(xi+a,b)的较大值就行了。

然后我们考虑这个标记如何合并,假设现在已经有了一个标记(a,b)" role="presentation" style="position: relative;">(a,b)(a,b),从父亲节点传过来了一个标记(c,d)" role="presentation" style="position: relative;">(c,d)(c,d),那么原本这个点的最大值是max(xi+a,b)" role="presentation" style="position: relative;">max(xi+a,b)max(xi+a,b),现在就变成了max(max(xi+a,b)+c,d)" role="presentation" style="position: relative;">max(max(xi+a,b)+c,d)max(max(xi+a,b)+c,d),也就是max(xi+a+b,b+c,d)" role="presentation" style="position: relative;">max(xi+a+b,b+c,d)max(xi+a+b,b+c,d),也就是max(xi+a+b,max(b+c,d))" role="presentation" style="position: relative;">max(xi+a+b,max(b+c,d))max(xi+a+b,max(b+c,d)),所以这个节点的标记就被更新成了(a+b,max(b+c,d))" role="presentation" style="position: relative;">(a+b,max(b+c,d))(a+b,max(b+c,d))。所以这个标记是可合并的。

然而,我们仍然不能够维护历史单点最大值,因此我们还需要维护一个(hisa,hisb)" role="presentation" style="position: relative;">(hisa,hisb)(hisa,hisb)标记,表示在上一次修改这个区间之后xi" role="presentation" style="position: relative;">xixi最大的历史增量和最大的历史取max" role="presentation" style="position: relative;">maxmax标记,最后同样是查询单点的(hisa,hisb)" role="presentation" style="position: relative;">(hisa,hisb)(hisa,hisb),然后输出(xi+hisa,hisb)" role="presentation" style="position: relative;">(xi+hisa,hisb)(xi+hisa,hisb)的较大值。

这时我们应该大胆的猜想一个节点的history" role="presentation" style="position: relative;">historyhistory标记也是可以合并的。仔细想想,如果从父亲节点传来了一个(a,b,hisa,hisb)" role="presentation" style="position: relative;">(a,b,hisa,hisb)(a,b,hisa,hisb),那么显然是用父亲的hisa" role="presentation" style="position: relative;">hisahisa和自己的a" role="presentation" style="position: relative;">aa来更新自己的hisa" role="presentation" style="position: relative;">hisahisa,然后用父亲的hisa" role="presentation" style="position: relative;">hisahisa和自己的b" role="presentation" style="position: relative;">bb,以及父亲的hisb" role="presentation" style="position: relative;">hisbhisb来更新自己的hisb" role="presentation" style="position: relative;">hisbhisb。

也就是hisa=max(hisa,fa.hisa+a),hisb=max(hisb,max(fa.hisb,fa.hisa+b))" role="presentation" style="position: relative;">hisa=max(hisa,fa.hisa+a),hisb=max(hisb,max(fa.hisb,fa.hisa+b))hisa=max(hisa,fa.hisa+a),hisb=max(hisb,max(fa.hisb,fa.hisa+b))

代码:

#include<bits/stdc++.h>
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
#define inf 1000000000000000000
#define N 500005
#define ll long long
using namespace std;
int n,m,op,l,r;
ll a[N],v;
struct Node{int l,r;ll a,b,his_a,his_b;}T[N<<2];
inline ll max(ll a,ll b){return a>b?a:b;}
inline void operator+=(Node&x,Node y){
    x.his_a=max(x.his_a,y.his_a+x.a);
    x.his_b=max(x.his_b,max(x.b+y.his_a,y.his_b));
    x.a=max(x.a+y.a,-inf);
    x.b=max(x.b+y.a,y.b);
}
inline void cle(int p){T[p].his_a=T[p].a=0,T[p].his_b=T[p].b=-inf;}
inline void pushdown(int p){T[lc]+=T[p],T[rc]+=T[p],cle(p);}
inline void build(int p,int l,int r){
    T[p].l=l,T[p].r=r,cle(p);
    if(l==r)return;
    build(lc,l,mid),build(rc,mid+1,r);
}
inline void update(int p,int ql,int qr,Node v){
    if(ql>T[p].r||qr<T[p].l)return;
    if(ql<=T[p].l&&T[p].r<=qr){T[p]+=v;return;}
    if(T[p].l!=T[p].r)pushdown(p);
    if(qr<=mid)update(lc,ql,qr,v);
    else if(ql>mid)update(rc,ql,qr,v);
    else update(lc,ql,mid,v),update(rc,mid+1,qr,v);
}
inline Node query(int p,int k){
    if(T[p].l==T[p].r)return T[p];
    pushdown(p);
    if(k<=mid)return query(lc,k);
    return query(rc,k);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
    build(1,1,n);
    while(m--){
        scanf("%d",&op);
        if(op==1)scanf("%d%d%lld",&l,&r,&v),update(1,l,r,(Node){l,r,v,-inf,v,-inf});
        if(op==2)scanf("%d%d%lld",&l,&r,&v),update(1,l,r,(Node){l,r,-v,0,-v,0});
        if(op==3)scanf("%d%d%lld",&l,&r,&v),update(1,l,r,(Node){l,r,-inf,v,-inf,v});
        if(op==4){scanf("%d",&l);Node t=query(1,l);printf("%lld\n",max(a[l]+t.a,t.b));}
        if(op==5){scanf("%d",&l);Node t=query(1,l);printf("%lld\n",max(a[l]+t.his_a,t.his_b));}
    }
    return 0;
}