P4314 CPU监控

P4314 CPU监控

好一道神仙题

思路来源于yyb神仙

题目要求我们支持4个操作:

区间赋值

区间加

区间最大值

区间历史最大值

这第四个操作简直是BUG一样的存在

思考一下,发现难点在于维护区间历史最大值

先打完这些:

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
#define INF 1050000000
struct TAG{
    #define il inline
    int a,b;
    il void clear(){a=0,b=-INF;}
    il int number(int x){return max(a+x,b);}
};
int n,m;
int a[N];
struct Sugment_Tree{//0:目前最大值 1:历史最大值 
    #define mid (l+r)/2
    #define il inline    
    Sugment_Tree(){
        
    }
    il void push_up(int num){
        
    }
    il void put_tag(int num,TAG a,TAG b){
        
    }
    il void push_down(int num){
        
    }
    il void build(int l,int r,int num){
        if(l==r){
            
        }
        build(l,mid,num<<1);
        build(mid+1,r,num<<1|1);
        push_up(num);
    }
    il void change(int l,int r,int num,int L,int R,TAG X){
        if(l>R||r<L) return;
        if(l>=L&&r<=R){
            
            return; 
        }
        push_down(num);
        change(l,mid,num<<1,L,R,X);
        change(mid+1,r,num<<1|1,L,R,X);
    }
    il int ask(int l,int r,int num,int L,int R,int opt){
        if(l>R||r<L) return 0;
        if(l>=L&&r<=R){
            
        }
        push_down(num);
        return max(ask(l,mid,num<<1,L,R,opt),ask(mid+1,r,num<<1|1,L,R,opt));
    }
}T;
char s[5];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    T.build(1,n,1);
    scanf("%d",&m);
    while(m--){
        int l,r,X;
        scanf("%s",s);
        if(s[0]=='Q'){
            scanf("%d%d",&l,&r);
            int p=T.ask(1,n,1,l,r,0);
            printf("%d\n",p);
        }
        else if(s[0]=='A'){
            scanf("%d%d",&l,&r);
            int p=T.ask(1,n,1,l,r,1);
            printf("%d\n",p);
        }
        else if(s[0]=='P'){
            scanf("%d%d%d",&l,&r,&X);
            TAG p;
            p.clear();
            p.a=X;
            T.change(1,n,1,l,r,p);
        }
        else{
            scanf("%d%d%d",&l,&r,&X);
            TAG p;
            p.clear();
            p.b=X;
            T.change(1,n,1,l,r,p);
        }
    }
    return 0;
} 

记住,inf不能太大,否则待会会爆int

然后开始一步一步想。

先给出神仙思路:

定义一个表示(a,b)表示区间内所有数先+a再和bmax,即x=max(x+a,b)
那么这样一来,区间加法转化成(a,),区间赋值变成了(,b)

这个思路的好处是将两种操作当成了一种来做

理论上,我们对每一个区间只要维护区间目前最大值、区间历史最大值、区间标记即可

但是历史最值还是维护不了。

考虑如何维护一个历史最值,我们对于每个点额外维护一个历史最值的标记,每次覆盖的时候不会覆盖掉历史最值的标记,只会用max操作更新历史最值标记。

接下来分段看

il void push_up(int num){
    t[num]=max(t[num<<1],t[num<<1|1]);
    hm[num]=max(hm[num<<1],hm[num<<1|1]);
}

这个push_up比较常规,不说

il void push_down(int num){
        put_tag(num<<1,tag[num],mtg[num]);
        put_tag(num<<1|1,tag[num],mtg[num]);
        mtg[num].clear();
        tag[num].clear();
    }

不妨让我们来思考一下:

这是一个下传图示

假设现在num<<1,num<<1|1上没有标记,num要下传

我们分别调用push_tag函数,合并标记

il void put_tag(int num,TAG a,TAG b){
        mtg[num]=max(mtg[num],tag[num]+b);
        tag[num]=tag[num]+a;
        hm[num]=max(hm[num],b.number(t[num]));
        t[num]=a.number(t[num]);
    }

精髓部分

第一行:mtg代表历史标记最值

这一行里我们所做的是维护历史最值标记

tag是当前标记

b是父亲节点的mtg

要是想要更新num的mtg,那么父亲节点必须选取mtg才最有可能

然后就有了第一行

+和max等下再说

第二行:tag代表目前标记

我们只需要把现在父亲传给num的标记合并上即可

第三行:hm表示历史最值

t表示当前最值

我们考虑,b是父亲的历史最值标记

这个标记有可能之前被修改了

b.number(x)代表x经过b操作之后的值与x比的较大值

说白了,就是试试看现在的num历史最值和当前num最值+b操作之后会不会改变历史

第四行:直接看看t会不会改变

TAG max(TAG x,TAG y){
    return (TAG){max(x.a,y.a),max(x.b,y.b)};
}
TAG operator+ (TAG x,TAG y){
    return (TAG){max(-INF,x.a+y.a),max(x.b+y.a,y.b)};
}

这两行就是标记的max与+

可以发现,max实际上就是最大的+拼上最大的max

比如:

操作 A(1,3) B(-2,6)

当前数组为1 4 2

什么都不做:最大值4 历史最大值4

做了A之后:最大值5 历史最大值5

做了B之后:最大值6 历史最大值6

对于两个标记如何合并max,是(max(a,x),max(b,y))。原因就是我们可以把这个标记看做是一个分段函数,那么这个合并就比较显然了。

那么取max后就是C(1,6)

(原因:max很好理解,而+是迟早要加的)

合并:

考虑先后顺序,加法并一起,max的话,分两种:先变max再加x或者直接变max(sum,y)

原因就是注意一下maxmax和加法的先后顺序。

il void change(int l,int r,int num,int L,int R,TAG X){
        if(l>R||r<L) return;
        if(l>=L&&r<=R){
            put_tag(num,X,X);
            return; 
        }
        push_down(num);
        change(l,mid,num<<1,L,R,X);
        change(mid+1,r,num<<1|1,L,R,X);
        push_up(num);
    }

线段树部分只有这个操作可说了。

为什么我们直接put_tag?

因为num的父亲也做了,然后拖到这一步做而已。

代码(完整)

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
#define INF 1050000000
struct TAG{
    #define il inline
    int a,b;
    il void clear(){a=0,b=-INF;}
    il int number(int x){return max(a+x,b);}
};
int n,m;
int a[N];

TAG max(TAG x,TAG y){
    return (TAG){max(x.a,y.a),max(x.b,y.b)};
}
TAG operator+ (TAG x,TAG y){
    return (TAG){max(-INF,x.a+y.a),max(x.b+y.a,y.b)};
}
struct Sugment_Tree{//0:目前最大值 1:历史最大值 
    #define mid (l+r)/2
    #define il inline
    TAG mtg[N<<2];//历史最值标记,每次覆盖的时候不会覆盖掉历史最值的标记,只会用max操作更新历史最值标记。 
    TAG tag[N<<2];//普通标记 
    int t[N<<2];//目前最值 
    int hm[N<<2];//历史最值 
    il void push_up(int num){
        t[num]=max(t[num<<1],t[num<<1|1]);
        hm[num]=max(hm[num<<1],hm[num<<1|1]);
    }
    il void put_tag(int num,TAG a,TAG b){
        mtg[num]=max(mtg[num],tag[num]+b);
        tag[num]=tag[num]+a;
        hm[num]=max(hm[num],b.number(t[num]));
        t[num]=a.number(t[num]);
    }
    il void push_down(int num){
        put_tag(num<<1,tag[num],mtg[num]);
        put_tag(num<<1|1,tag[num],mtg[num]);
        mtg[num].clear();
        tag[num].clear();
    }
    il void build(int l,int r,int num){
        mtg[num].clear();
        tag[num].clear();
        if(l==r){
            t[num]=hm[num]=a[l];
            return;
        }
        build(l,mid,num<<1);
        build(mid+1,r,num<<1|1);
        push_up(num);
    }
    il void change(int l,int r,int num,int L,int R,TAG X){
        if(l>R||r<L) return;
        if(l>=L&&r<=R){
            put_tag(num,X,X);
            return; 
        }
        push_down(num);
        change(l,mid,num<<1,L,R,X);
        change(mid+1,r,num<<1|1,L,R,X);
        push_up(num);
    }
    il int ask(int l,int r,int num,int L,int R,int opt){
        if(l>R||r<L) return -INF;
        if(l>=L&&r<=R){
            
            return opt?hm[num]:t[num];
        }
        push_down(num);
        return max(ask(l,mid,num<<1,L,R,opt),ask(mid+1,r,num<<1|1,L,R,opt));
    }
}T;
char s[5];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    T.build(1,n,1);
    scanf("%d",&m);
    while(m--){
    //    printf("%d &&&&&&&&&&&&&&&&&\n",T.hm[15]);
        int l,r,X;
        scanf("%s",s);
        TAG p=(TAG){-INF,-INF};
        if(s[0]=='Q'){
            scanf("%d%d",&l,&r);
            int p=T.ask(1,n,1,l,r,0);
            printf("%d\n",p);
        }
        else if(s[0]=='A'){
            scanf("%d%d",&l,&r);
            int p=T.ask(1,n,1,l,r,1);
            printf("%d\n",p);
        }
        else if(s[0]=='P'){
            scanf("%d%d%d",&l,&r,&X);
            
            p.a=X;
            T.change(1,n,1,l,r,p);
        }
        else{
            scanf("%d%d%d",&l,&r,&X);
            
            p.b=X;
            T.change(1,n,1,l,r,p);
        }
    }
    return 0;
} 

 

posted @ 2019-10-27 17:39  QYJ060604  阅读(250)  评论(0编辑  收藏  举报