题目大意
初始给出n个数,a[1]~a[n]
有m个操作
1,把a[l]~a[r]加上x
2,把a[l]~a[r]减去x后和0取max
3,把a[l]~a[r]和x取max
4,输出a[x]
5,输出a[x]的历史最大值(即出现过的值的最大值)
数据范围,n,m<=5*10^5,x<=10^9
设计标记
令标记(a,b)表示对于x进行max(x+a,b)那么
操作1相当于(x,-inf)
2相当于(-x,0)
3相当于(0,x)
合并标记
如果一个数x先经过(a,b),再经过(c,d),会怎样呢?
x=max(max((a+x),b)+c,d)=max(a+c+x,max(b+c,d))
所以可以把标记合并为(a+c,max(b+c,d))
如何处理历史最大值呢?
相当于记录一个历史最大值标记,无论x的值为何,经过这个标记一定是历史最大值。
其实可以把一个标记看做一个一次函数和一个与x轴平行的函数取max,每次操作i记做标记fi,设Fi=f0~i合并后的标记
那历史最大值标记肯定是(max(Fi_a),max(Fi_b))
用线段树来维护这些标记,对于线段树上的一个节点,假如当前它存在有标记,他的儿子的标记的时间显然是比它要先的,否则它的标记肯定已经下传了,
它的儿子的历史最大值标记应该由它的历史最大标记和它的儿子的当前标记合并后再拿去更新(因为它的Fi=fj+1~i
(j<i)
,而它的儿子的Fj=fj’+1~j
(j′<j)
,所以它历史最大值标记和它的儿子相比,缺少fj’+1~j,所以和Fj合并即可)
,当前标记应该要和它的当前标记合并.
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define inf 1e17
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b)for(i=a;i>=b;i--)
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
using namespace std;
const int maxn=5e5+5;
int ww;char ch;
inline int read(){
ww=0;
for(ch=getchar();ch<'0'||ch>'9';) ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) ww=ww*10+ch-48;
return ww;
}
int i,j,n,m,x;ll a[maxn];
struct ar{
ll a,b,ma,mb;
}tree[maxn*4],p,ze=(ar){0,-inf,0,-inf};
ar operator +=(ar &a,ar p){
a.ma=max(a.ma,a.a+p.ma);
a.mb=max(a.mb,max(a.b+p.ma,p.mb));
a.a=max(a.a+p.a,-inf);
a.b=max(a.b+p.a,p.b);
}
void down(int k){
int lk=k<<1,rk=lk+1;
tree[lk]+=tree[k],tree[rk]+=tree[k];
tree[k]=ze;
}
void change(int k,int l,int r,int a,int b){
if (l!=r)down(k);
if (l==a&&r==b){
tree[k]+=p;
return;
}
int m=(l+r)>>1,lk=k<<1,rk=lk+1;
if (b<=m) change(lk,l,m,a,b);else
if (a>m) change(rk,m+1,r,a,b);else
change(lk,l,m,a,m),change(rk,m+1,r,m+1,b);
}
void find(int k,int l,int r){
if (l==r) {p=tree[k];return;}
down(k);
int m=(l+r)>>1,lk=k<<1,rk=lk+1;
if (x<=m) find(lk,l,m);else find(rk,m+1,r);
}
int main(){
n=read(),m=read();
fo(i,1,n) a[i]=read();
fo(i,1,4*n) tree[i]=ze;
while (m--){
int o=read(),l,r;
if (o<=3){
l=read(),r=read(),x=read();
if (o==1) p=(ar){x,-inf,x,-inf};else
if (o==2) p=(ar){-x,0,-x,0};else
p=(ar){-inf,x,-inf,x};
change(1,1,n,l,r);
}else{
x=read(),find(1,1,n);
if (o==4) printf("%lld\n",max(a[x]+p.a,p.b));else
printf("%lld\n",max(a[x]+p.ma,p.mb));
}
}
}