题意:
给你一个长度为n的数组a,定义f:
每次有两种操作:
1 l r x 让你在l~r区间中选一个位置,将那个位置的a值+x之后,f最大是多少
2 l r x 将区间l~r的所有a值+x
题解:
区间问题,首先考虑数据结构
由于它是绝对值的和,所以要分成很多情况,写起来好累
我们可以知道线段树维护的是a[i]-a[i+1],假设经过处理的数组数b
对于1操作,假设我们要加的位置是p,那么我们要尽量让b[p-1]尽量<=0,b[p]尽量>=0,因为这个样子我们能够加的值就越大。
于是我们mi数组维护需要减掉的最小值:
mi[root]=max(0ll,a[l-1])+max(0ll,-a[l]);
这时候出现了一个问题,有可能会出现这样子的点:(假设黑色线是零线)
第b[p]<0,b[p-1]>0并且就算加了x之后正负号还是不变,这时候答案就会-2.
但是仔细观察,这种情况只会出现在l=r的时候,因为对于相邻的p+1,p-1这两个位置,就不会是这种情况,所以当r>l的时候,最小值一定是0.
最后在更新的时候要注意更新的是4个点。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+5;
ll sum[N*4],mi[N*4],lef[N*4],rig[N*4];
ll a[N];
ll mx(ll v){return max(v,-v);}
void push_up(int root){
sum[root]=sum[root<<1]+sum[root<<1|1];
mi[root]=min(mi[root<<1],mi[root<<1|1]);
}
void build(int l,int r,int root){
if(l==r){
sum[root]=mx(a[l]);
if(l-1)
mi[root]=max(0ll,a[l-1])+max(0ll,-a[l]);
return ;
}
int mid=l+r>>1;
build(l,mid,root<<1);
build(mid+1,r,root<<1|1);
push_up(root);
}
int n;
void update(int l,int r,int root,int p,ll v){
if(l==r){
a[l]+=v;
sum[root]=mx(a[l]);
if(p-1)
mi[root]=max(0ll,a[l-1])+max(0ll,-a[l]);
return ;
}
int mid=l+r>>1;
if(mid>=p)
update(l,mid,root<<1,p,v);
else
update(mid+1,r,root<<1|1,p,v);
push_up(root);
}
ll query(int l,int r,int root,int ql,int qr){
if(l>=ql&&r<=qr)
return mi[root];
ll ans=1e18;
int mid=l+r>>1;
if(mid>=ql)
ans=query(l,mid,root<<1,ql,qr);
if(mid<qr)
ans=min(ans,query(mid+1,r,root<<1|1,ql,qr));
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),a[i-1]-=a[i];
build(1,n-1,1);
int q;
scanf("%d",&q);
while(q--){
int op,l,r;
ll x;
scanf("%d%d%d%lld",&op,&l,&r,&x);
if(op-2){
if(l==r)
printf("%lld\n",sum[1]-mx(a[l-1])+mx(a[l-1]-x)-mx(a[l])+mx(a[l]+x));
else
printf("%lld\n",sum[1]+max(0ll,2*x-2*query(1,n-1,1,l,r)));
}
else{
update(1,n-1,1,l-1,-x),update(1,n-1,1,r,x);
if(l+1<n)update(1,n-1,1,l,0);
if(r+1<n)update(1,n-1,1,r+1,0);
}
}
return 0;
}