• bzoj3064/洛谷P4314 CPU监控【线段树】


    好,长草博客被催更了【?】

    我感觉这题完全可以当作线段树3

    线段树2考加法和乘法标记的下放顺序,这道题更丧心病狂【?】

    很多人可能跟我一样,刚看到这道题秒出思路:打一个当前最大值一个历史最大值不就完事了吗

    实际上这样做会死得很惨。节点保留的信息可能来不及下传就被父节点更新掉,导致一部分信息被覆盖而丢失,这样就有可能查不到正确的历史最大值。比如历史最大值是由add更新的,但是set覆盖下来把add变成了0。

    为了保留这些信息,我们需要记录历史修改标记的最大值。标记hset(history-set)记录从上一次pushdown到现在set标记的最大值,hadd(history-add)记录从上一次pushdown到现在add标记达到过的最大值(不是add每次增加的最大值)。hset和hadd不是用来更新自身的hmax值的,而是用来在pushdown的时候下放,更新儿子的pushdown。仔细想一下就可以知道,当前节点保留的hset,hadd,与儿子存的各种当前信息(max,add,set)结合,都是儿子曾经能够达到的状态,更新合法且一定能更新出儿子准确的历史最大值。

    因为pushdown的时候也要下放hset和hadd,所以只记录每次pushdown到现在的值。具体的下放顺序我写在代码注释里了。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int n,m;
    char c[5];
    long long a[100010];
    const long long inf=2147483647;//初始化用 
    struct node{
        int l,r;
        long long hmax,max,hadd,hset,add,set;//哪个代表什么很显然吧XD 
    }b[400010];
    void build(int p,int l,int r){
        b[p].l=l,b[p].r=r;
        if(l==r){
            b[p].hmax=b[p].max=a[l];
            return;
        }
        int mid=(l+r)/2;
        b[p*2].hset=b[p*2].set=b[p*2].hmax=b[p*2].max=-inf;//初始化左右儿子,1的我写在主函数里了 
        b[p*2+1].hset=b[p*2+1].set=b[p*2+1].hmax=b[p*2+1].max=-inf;
        build(p*2,l,mid);
        build(p*2+1,mid+1,r);
        b[p].max=max(b[p*2].max,b[p*2+1].max);
        b[p].hmax=max(b[p*2].max,b[p*2+1].max);
    }
    void pushdown(int p){//核心操作,按顺序分类讨论 
        //写标记的习惯一般是,当前节点有标记=当前节点已经被更新好所有信息(hmax,max),
        //保有可以下传而不对自己造成影响的修改标记(add,hadd,set,hset)
        //pushdown的时候,让左右儿子也达到这个要求,自己清空所有修改标记,
        //这样自己在成为修改函数最终目标的时候就可以直接更新所有信息。 
        b[p*2].hmax=max(b[p*2].hmax,max(b[p].hset,b[p].hadd+b[p*2].max));//先用儿子在这次pushdown之前就保留的信息更新历史最大值 
        if(b[p*2].set!=-inf)b[p*2].hset=max(b[p*2].hset,b[p*2].set+b[p].hadd);//下放当前节点的hadd。儿子保有的set一定是在自身的hadd之前被打上的 
        else b[p*2].hadd=max(b[p*2].hadd,b[p].hadd+b[p*2].add);//同理,儿子的add也是在自身的hadd之前被打上的 
        b[p*2].hset=max(b[p*2].hset,b[p].hset);//下放hset
        if(b[p].add){//下放add 
            if(b[p*2].set!=-inf)b[p*2].set+=b[p].add;//如果儿子有set,就把add累计到set上 
            else b[p*2].add+=b[p].add;
            b[p*2].max+=b[p].add;
        }
        if(b[p].set!=-inf){//下放set,直接覆盖 
            b[p*2].max=b[p*2].set=b[p].set;
            b[p*2].add=0;//清零儿子的add,因为被直接覆盖掉了 
        }
        b[p*2].hset=max(b[p*2].hset,b[p*2].set);//用儿子更新过以后的信息来更新hset和hadd 
        b[p*2].hadd=max(b[p*2].hadd,b[p*2].add);
        
        b[p*2+1].hmax=max(b[p*2+1].hmax,max(b[p].hset,b[p].hadd+b[p*2+1].max));//复制一遍 
        if(b[p*2+1].set!=-inf)b[p*2+1].hset=max(b[p*2+1].hset,b[p*2+1].set+b[p].hadd);
        else b[p*2+1].hadd=max(b[p*2+1].hadd,b[p].hadd+b[p*2+1].add);
        b[p*2+1].hset=max(b[p*2+1].hset,b[p].hset);
        if(b[p].add){
            if(b[p*2+1].set!=-inf)b[p*2+1].set+=b[p].add;
            else b[p*2+1].add+=b[p].add;
            b[p*2+1].max+=b[p].add;
        }
        if(b[p].set!=-inf){
            b[p*2+1].max=b[p*2+1].set=b[p].set;
            b[p*2+1].add=0;
        }
        b[p*2+1].hset=max(b[p*2+1].hset,b[p*2+1].set);
        b[p*2+1].hadd=max(b[p*2+1].hadd,b[p*2+1].add);
        b[p].set=b[p].hset=-inf;
        b[p].add=b[p].hadd=0;
    }
    long long ask1(int p,int l,int r){//询问当前最大值,常规操作 
        if(b[p].l!=b[p].r)pushdown(p);
        if(l<=b[p].l&&b[p].r<=r){
            return b[p].max;
        }
        int mid=(b[p].l+b[p].r)/2;
        long long val=-inf;
        if(l<=mid)val=max(val,ask1(p*2,l,r));
        if(r>mid)val=max(val,ask1(p*2+1,l,r));
        b[p].max=max(b[p*2].max,b[p*2+1].max);
        b[p].hmax=max(b[p*2].hmax,b[p*2+1].hmax);
        return val;
    }
    long long ask2(int p,int l,int r){//询问历史最大值,常规操作 
        if(b[p].l!=b[p].r)pushdown(p);
        if(l<=b[p].l&&b[p].r<=r){
            return b[p].hmax;
        }
        int mid=(b[p].l+b[p].r)/2;
        long long val=-inf;
        if(l<=mid)val=max(val,ask2(p*2,l,r));
        if(r>mid)val=max(val,ask2(p*2+1,l,r));
        b[p].max=max(b[p*2].max,b[p*2+1].max);
        b[p].hmax=max(b[p*2].hmax,b[p*2+1].hmax);
        return val;
    }
    void change1(int p,int l,int r,long long val){//add
        if(b[p].l!=b[p].r)pushdown(p);//到达之前就pushdown 
        if(l<=b[p].l&&b[p].r<=r){
            b[p].add+=val;//因为已经pushdown过了,所以这里直接更新所有信息 
            b[p].hadd+=val;
            b[p].max+=val;
            b[p].hmax=max(b[p].hmax,b[p].max);
            return;
        }
        int mid=(b[p].l+b[p].r)/2;
        if(l<=mid)change1(p*2,l,r,val);
        if(r>mid)change1(p*2+1,l,r,val);
        b[p].max=max(b[p*2].max,b[p*2+1].max);
        b[p].hmax=max(b[p*2].hmax,b[p*2+1].hmax);
    }
    void change2(int p,int l,int r,long long val){//set
        if(b[p].l!=b[p].r)pushdown(p);//到达之前pushdown 
        if(l<=b[p].l&&b[p].r<=r){
            b[p].max=b[p].hset=b[p].set=val;//同样,因为已经pushdown过了,直接更新所有信息 
            b[p].hmax=max(b[p].hmax,b[p].max);
            return;
        }
        int mid=(b[p].l+b[p].r)/2;
        if(l<=mid)change2(p*2,l,r,val);
        if(r>mid)change2(p*2+1,l,r,val);
        b[p].max=max(b[p*2].max,b[p*2+1].max);
        b[p].hmax=max(b[p*2].hmax,b[p*2+1].hmax);
    }
    int main()//主函数都是常规操作 
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
        b[1].hset=b[1].set=b[1].hmax=b[1].max=-inf;
        build(1,1,n);
        scanf("%d",&m);
        while(m--){
            scanf("%s",c);
            int x,y;
            long long z;
            if(c[0]=='Q'){
                scanf("%d%d",&x,&y);
                printf("%lld
    ",ask1(1,x,y));
            }
            else if(c[0]=='A'){
                scanf("%d%d",&x,&y);
                printf("%lld
    ",ask2(1,x,y));
            }
            else if(c[0]=='P'){
                scanf("%d%d%lld",&x,&y,&z);
                change1(1,x,y,z);
            }
            else{
                scanf("%d%d%lld",&x,&y,&z);
                change2(1,x,y,z);
            }
        }
        return 0;
    }

     这道题真的非常有价值,希望看到的同学都不要放弃,努力写一下。就算是参考着题解写的也十分有意义_(:з」∠)_。

    思路参考了洛谷题解区枫林晚的做法。

  • 相关阅读:
    进程状态
    VMware虚拟机的三种联网方法及原理
    关于C++迭代器失效
    头文件:limits.h、float.h
    正则表达式之一:元符号
    MYSQL之批量插入数据库
    PHP之如何判断数字(数字字符串不算)
    使用Process Monitor来得到程序运行参数
    Abusing the C preprocessor
    1+1还是1+1=2?
  • 原文地址:https://www.cnblogs.com/chloris/p/11243403.html
Copyright © 2020-2023  润新知