题意:给你n个数,定义f(a)为
有两种操作:
①在区间[L,R]选一个数加上x,使得f(a)最大,该操作不影响原数组。
②向一段区间[L,R]全部加上x。
题解:首先我们先得出所有的差值,对于每一次询问①,假设在i这个位置上的数加上x,那么会影响到cha[i-1]与cha[i],然后我们考虑cha[i-1]与cha[i]对答案的影响,可以分为两种:
①cha[i-1]<=0或者cha[i]>=0,
②cha[i-1]>0&&cha[i]<0.
对于贡献开始增加的临界值x是:
ll now=0;
if(cha[L-1]>0)now+=cha[L-1];
if(cha[L]<0)now-=cha[L];
对于第一张种情况贡献始终是大于等于0的。
对于第二种情况,是存在贡献为负的,之后贡献一直增加,但是我们都可简单推论,只有当询问区间L==R的时候才可能取到贡献为负的情况,不然肯定是取第一种情况的位置。因此我们用线段树记录cha[i-1]与cha[i]对答案贡献的临界值,若l==r则直接计算答案,否则找到区间的最小值,若临界值的最小值是>x则sum不变,否则sum=sum+(x-mi)*2.
对于第二种操作,其实就只是改变了cha[l-1]与cha[r],线段树更新即可。
AC代码:
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
typedef long long ll;
ll a[100005],cha[100005];
ll tree[100005*4],n;
void update(ll pos,ll L,ll R,ll root)
{
if(L==R)
{
ll now=0;
if(cha[L-1]>0)now+=cha[L-1];
if(cha[L]<0)now-=cha[L];
tree[root]=now;
return ;
}
ll mid=L+R>>1;
if(pos<=mid)update(pos,L,mid,root<<1);
else update(pos,mid+1,R,root<<1|1);
tree[root]=min(tree[root<<1],tree[root<<1|1]);
}
ll query(ll l,ll r,ll L,ll R,ll root)
{
if(l<=L&&R<=r)return tree[root];
ll mid=L+R>>1;
if(r<=mid)return query(l,r,L,mid,root<<1);
else if(l>mid)return query(l,r,mid+1,R,root<<1|1);
else return min(query(l,mid,L,mid,root<<1),query(mid+1,r,mid+1,R,root<<1|1));
}
int main()
{
scanf("%lld",&n);
ll sum=0;
for(ll i=0;i<n;i++)
scanf("%lld",&a[i]);
for(ll i=0;i<n-1;i++)
{
cha[i]=a[i]-a[i+1],sum+=abs(cha[i]);
if(i>0)update(i,1,n-2,1);
}
ll q;
scanf("%lld",&q);
while(q--)
{
ll op,l,r,x;
scanf("%lld%lld%lld%lld",&op,&l,&r,&x);
l--,r--;
if(op==1)
{
if(l==r)
{
printf("%lld\n",sum-abs(cha[l-1])-abs(cha[l])+abs(cha[l-1]-x)+abs(cha[l]+x));
continue;
}
ll s=query(l,r,1,n-2,1);
if(s>x)printf("%lld\n",sum);
else printf("%lld\n",sum+(x-s)*2);
}
else
{
sum=sum-abs(cha[l-1])-abs(cha[r]);
cha[l-1]-=x;
cha[r]+=x;
sum=sum+abs(cha[l-1])+abs(cha[r]);
update(l,1,n-2,1);
update(r,1,n-2,1);
if(l-1>=1)update(l-1,1,n-2,1);
if(r+1<=n-2)update(r+1,1,n-2,1);
}
}
}