传送门
这题真是一道恶心的线段树
需要维护的值有6个:当前最大值,当前的加法标记,当前的覆盖标记;历史最大值,历史最大的加法标记,历史最大的覆盖标记
其实说历史最大的加法和覆盖标记并不是那么准确,更准确地说应该是 现在能用来更新子树的最大值
维护当前的一系列量都比较好写,都是线段树的基本操作
但是维护历史的一系列量更新就比较麻烦了,首先要知道的是维护历史的量一定分别对应大于等于维护当前的量,具体地:
更新子树的历史最大值:考虑这个区间的历史加法和历史覆盖,以及子树的当前最大值
更新子树的历史加法:考虑这个区间的历史加法和子树的当前加法
更新子树的历史覆盖:考虑这个区间的当前覆盖和子树的历史覆盖
值得注意的是如果子树当前没有覆盖,那么可以直接更新加法,否则应该把加法加到覆盖上
还有,每到一层区间就直接下放标记,无论区间是否完全覆盖
代码还是可读的,下划线系列是历史
#include
#include
#include
#include
#include
using namespace std;
#define N 100005
#define LL long long
const LL inf=1e18;
int n,m,a[N];
LL maxn[N*4],add[N*4],cover[N*4],_maxn[N*4],_add[N*4],_cover[N*4];
void update(int now)
{
maxn[now]=max(maxn[now<<1],maxn[now<<1|1]);
_maxn[now]=max(_maxn[now<<1],_maxn[now<<1|1]);
}
void pushdown(int now)
{
int son;
for (int i=0;i<=1;++i)
{
son=now<<1|i;
_maxn[son]=max(_maxn[son],max(_cover[now],maxn[son]+_add[now]));
if (cover[son]==-inf) _add[son]=max(_add[son],add[son]+_add[now]);
else _cover[son]=max(_cover[son],cover[son]+_add[now]);
if (add[now])
{
if (cover[son]!=-inf) cover[son]+=add[now];
else add[son]+=add[now];
maxn[son]+=add[now];
}
if (cover[now]!=-inf)
{
maxn[son]=cover[son]=cover[now];
add[son]=0;
}
_cover[son]=max(_cover[son],max(cover[son],_cover[now]));
_add[son]=max(_add[son],add[son]);
}
add[now]=_add[now]=0;
cover[now]=_cover[now]=-inf;
}
void build(int now,int l,int r)
{
int mid=(l+r)>>1;
add[now]=_add[now]=0;
cover[now]=_cover[now]=-inf;
if (l==r)
{
maxn[now]=(LL)a[l];
_maxn[now]=(LL)a[l];
return;
}
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
void change(int now,int l,int r,int lr,int rr,LL x,int opt)
{
if (l!=r) pushdown(now);
int mid=(l+r)>>1;
if (lr<=l&&r<=rr)
{
if (!opt) maxn[now]+=x,add[now]+=x,_add[now]+=x;
else cover[now]=_cover[now]=maxn[now]=x;
_maxn[now]=max(_maxn[now],maxn[now]);
return;
}
if (lr<=mid) change(now<<1,l,mid,lr,rr,x,opt);
if (mid+1<=rr) change(now<<1|1,mid+1,r,lr,rr,x,opt);
update(now);
}
LL query(int now,int l,int r,int lr,int rr,int opt)
{
if (l!=r) pushdown(now);
int mid=(l+r)>>1;
LL ans=-inf;
if (lr<=l&&r<=rr) return (opt?_maxn[now]:maxn[now]);
if (lr<=mid) ans=max(ans,query(now<<1,l,mid,lr,rr,opt));
if (mid+1<=rr) ans=max(ans,query(now<<1|1,mid+1,r,lr,rr,opt));
return ans;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&m);
for (int i=1;i<=m;++i)
{
char opt=getchar();
while (opt!='Q'&&opt!='A'&&opt!='P'&&opt!='C') opt=getchar();
int l,r,x;LL ans;
if (opt=='Q')
{
scanf("%d%d",&l,&r);
ans=query(1,1,n,l,r,0);
printf("%lld\n",ans);
}
else if (opt=='A')
{
scanf("%d%d",&l,&r);
ans=query(1,1,n,l,r,1);
printf("%lld\n",ans);
}
else if (opt=='P')
{
scanf("%d%d%d",&l,&r,&x);
change(1,1,n,l,r,(LL)x,0);
}
else if (opt=='C')
{
scanf("%d%d%d",&l,&r,&x);
change(1,1,n,l,r,(LL)x,1);
}
}
}