题目大意
给定一个长度为
n
的序列
∙ A x y
:询问序列上区间
[x,y]
的历史最大值
∙ P x y z
:将序列上区间
[x,y]
的值都增加
z
题目分析
参考吉如一的2016年国家集训队论文《区间最值操作与历史最值问题》,里面讲得很清楚了。
一个重要的思路是,在一个点没有被赋值操作影响时,只需要维护区间加标记,在一个点被赋值操作影响过之后,所有操作都可以看成赋值操作。
线段树每个点维护四个标记,两个是正常的区间加与赋值标记,还有两个,分别代表一个点在上一次标记下传至今最大的区间加标记和赋值标记。然后各种讨论合并。细节自己脑补吧。
时间复杂度
O(nlogn)
代码实现
标记处理代码比线段树长系列。
#include <iostream>
#include <climits>
#include <cstdio>
#include <cctype>
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
int buf[30];
void write(int x)
{
if (x<0) putchar('-'),x=-x;
for (;x;x/=10) buf[++buf[0]]=x%10;
if (!buf[0]) buf[++buf[0]]=0;
for (;buf[0];putchar('0'+buf[buf[0]--]));
}
const int INF=INT_MAX;
const int N=100050;
int a[N];
int n,q;
struct segment_tree
{
int pre[N<<2][2],tag[N<<2][2];
int h[N<<2],v[N<<2];
void add_(int x,int delta)
{
h[x]=max(h[x],v[x]+delta);
if (tag[x][1]!=-INF) pre[x][1]=max(pre[x][1],tag[x][1]+delta);
else pre[x][0]=max(pre[x][0],tag[x][0]+delta);
}
void assign_(int x,int edit){h[x]=max(h[x],edit),pre[x][1]=max(pre[x][1],edit);}
void add(int x,int delta)
{
h[x]=max(h[x],v[x]+=delta);
if (tag[x][1]!=-INF) pre[x][1]=max(pre[x][1],tag[x][1]+=delta);
else pre[x][0]=max(pre[x][0],tag[x][0]+=delta);
}
void assign(int x,int edit){h[x]=max(h[x],v[x]=edit),tag[x][1]=edit,pre[x][1]=max(pre[x][1],edit);}
void clear(int x,int l,int r)
{
if (pre[x][0])
{
if (l!=r) add_(x<<1,pre[x][0]),add_(x<<1|1,pre[x][0]);
pre[x][0]=0;
}
if (pre[x][1]!=-INF)
{
if (l!=r) assign_(x<<1,pre[x][1]),assign_(x<<1|1,pre[x][1]);
pre[x][1]=-INF;
}
if (tag[x][0])
{
if (l!=r) add(x<<1,tag[x][0]),add(x<<1|1,tag[x][0]);
tag[x][0]=0;
}
if (tag[x][1]!=-INF)
{
if (l!=r) assign(x<<1,tag[x][1]),assign(x<<1|1,tag[x][1]);
tag[x][1]=-INF;
}
}
void update(int x){v[x]=max(v[x<<1],v[x<<1|1]),h[x]=max(h[x<<1],h[x<<1|1]);}
void modify(int x,int st,int en,int l,int r,int y,bool tp)
{
clear(x,l,r);
if (st==l&&en==r)
{
if (tp) assign(x,y);
else add(x,y);
clear(x,l,r);
return;
}
int mid=l+r>>1;
if (en<=mid) modify(x<<1,st,en,l,mid,y,tp);
else if (mid+1<=st) modify(x<<1|1,st,en,mid+1,r,y,tp);
else modify(x<<1,st,mid,l,mid,y,tp),modify(x<<1|1,mid+1,en,mid+1,r,y,tp);
update(x);
}
int query(int x,int st,int en,int l,int r,bool tp)
{
clear(x,l,r);
if (st==l&&en==r) return tp?h[x]:v[x];
int mid=l+r>>1;
if (en<=mid) return query(x<<1,st,en,l,mid,tp);
else if (mid+1<=st) return query(x<<1|1,st,en,mid+1,r,tp);
else return max(query(x<<1,st,mid,l,mid,tp),query(x<<1|1,mid+1,en,mid+1,r,tp));
}
void build(int x,int l,int r)
{
pre[x][0]=tag[x][0]=0,pre[x][1]=tag[x][1]=-INF;
if (l==r)
{
h[x]=v[x]=a[l];
return;
}
int mid=l+r>>1;
build(x<<1,l,mid),build(x<<1|1,mid+1,r),update(x);
}
}t;
int main()
{
freopen("monitor.in","r",stdin),freopen("monitor.out","w",stdout);
n=read();
for (int i=1;i<=n;++i) a[i]=read();
t.build(1,1,n);
for (q=read();q--;)
{
char opt=getchar();
while (!isalpha(opt)) opt=getchar();
int x=read(),y=read();
if (opt=='Q'||opt=='A') write(t.query(1,x,y,1,n,opt=='A')),putchar('\n');
else t.modify(1,x,y,1,n,read(),opt=='C');
}
fclose(stdin),fclose(stdout);
return 0;
}