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\n",ask1(1,x,y));
        }
        else if(c[0]=='A'){
            scanf("%d%d",&x,&y);
            printf("%lld\n",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;
}

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

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

posted @ 2019-07-25 16:42  Chloris_Black  阅读(183)  评论(2编辑  收藏  举报