题目链接:V
这道题由于是单点询问,所以异常好写。
注意到每种修改操作都可以用一个标记((a,b))表示。标记((a,b))的意义就是(x= max{x+a,b})
同时这种标记也是支持合并的。有((a,b)+(c,d)=(a+c,max{b+c,d}))
用上这种标记的话,(1)操作就是((x,0)),(2)操作就是((-x,0)),(3)操作就是((-inf,x))。
要查询单点值的话只要把所有标记都下放了就好了。
这种标记也支持取(max)。即,(max{(a,b),(c,d)}=(max{a,c},max{b,d}))。本质上就是一个分段函数取(max)的过程。
所以最后一问再维护一个历史最大值这道题就做完了。注意下传标记时先传历史标记。
还有一个要注意的地方:标记((a,b))里的(a)实际上是(max{a,-inf})。注意修改的时候和(-inf)取个(max),不然很容易就爆掉了。
下面贴代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) #define max(a,b) (a>b?a:b) #define maxn 500010 #define INF (1LL<<60) using namespace std; typedef long long llg; int n,m,L,R; llg na[maxn<<2],nb[maxn<<2]; llg pa[maxn<<2],pb[maxn<<2],za,zb; int getint(){ int w=0;bool q=0; char c=getchar(); while((c>'9'||c<'0')&&c!='-') c=getchar(); if(c=='-') c=getchar(),q=1; while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } void pushdown(int u){ for(int i=0,v;v=u<<1|i,i<2;i++){ pa[v]=max(pa[v],pa[u]+na[v]); pb[v]=max(pb[v],max(pb[u],nb[v]+pa[u])); na[v]=max(na[v]+na[u],-INF); nb[v]=max(nb[v]+na[u],nb[u]); } na[u]=nb[u]=pa[u]=pb[u]=0; } void build(int u,int l,int r){ int lc=u<<1,lv=u<<1|1,mid=(l+r)>>1; if(l==r) na[u]=pa[u]=getint(); else build(lc,l,mid),build(lv,mid+1,r); } void add(int u,int l,int r){ int lc=u<<1,lv=u<<1|1,mid=(l+r)>>1; if(L<=l && r<=R){ na[u]=max(na[u]+za,-INF); nb[u]=max(nb[u]+za,zb); pa[u]=max(pa[u],na[u]); pb[u]=max(pb[u],nb[u]); return; } pushdown(u); if(L<=mid) add(lc,l,mid); if(R>mid) add(lv,mid+1,r); } void query(bool w){ int u=1,l=1,r=n,mid; while(l!=r){ mid=(l+r)>>1; pushdown(u); u<<=1; if(L<=mid) r=mid; else u|=1,l=mid+1; } za=w?na[u]:pa[u]; zb=w?nb[u]:pb[u]; } int main(){ File("a"); n=getint(),m=getint(); build(1,1,n); while(m--){ int ty=getint(),x; if(ty<=3){ L=getint(),R=getint();x=getint(); if(ty==1) za=x,zb=0; if(ty==2) za=-x,zb=0; if(ty==3) za=-INF,zb=x; add(1,1,n); } else{ L=R=getint(); query(ty==4); printf("%lld ",max(za,zb)); } } return 0; }