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