[ 线段树 树状数组 ] Codeforces935F Fafa and Array

先考虑询问。
对于边界的点直接更新答案。
对于不在边界上的点:

  • ai1ai a i − 1 ≤ a i ai+1ai a i + 1 ≤ a i ,增大 ai a i 时答案的增加量为 2x 2 x
  • ai a i ai1 a i − 1 ai+1 a i + 1 之间,设 t=max(ai1,ai+1)ai t = max ( a i − 1 , a i + 1 ) − a i ,若 xt x ≤ t 增大 ai a i 时答案不变,否则答案的增加量为 2(xt) 2 ( x − t )
  • ai<ai1 a i < a i − 1 ai<ai+1 a i < a i + 1 ,增大 ai a i 时答案的增加量需要讨论。

发现如果存在第一种情况,选第一种肯定是最优的,可以用线段树记录是否存在。
如果不存在, [l,r] [ l , r ] 一定是单调或下凸的,也就是说第三种情况最多只会出现一次,找到之后讨论一下就好了。
第二种情况可以用线段树维护最小的 t t
对于修改操作,显然只会影响到 al1,al,ar,ar+1 ,直接修改就好了 。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
const ll INF=1e18;
int k,n,m,q,l,r,x;
int a[N];
ll A[N],d[N],sum;
ll c[N<<2][2];
int pos[N<<2];
inline void Update_node(int x,int y){
    for(;x<=n;x+=x&-x)A[x]+=y;
}
inline ll Query_node(int x){
    ll Ans=0;
    for(;x;x-=x&-x)Ans+=A[x];
    return Ans;
}
inline ll Abs(ll x){
    return x>0?x:-x;
}
inline void Up(int x){
    c[x][0]=min(c[x<<1][0],c[x<<1|1][0]);
    c[x][1]=max(c[x<<1][1],c[x<<1|1][1]);
    if(pos[x<<1]!=-1)pos[x]=pos[x<<1];else pos[x]=pos[x<<1|1];
}
void Build(int x,int l,int r){
    if(l==r){
        if(l<n&&l>1&&!(a[l]<a[l-1]&&a[l]<a[l+1]))c[x][0]=max(a[l+1]-a[l],a[l-1]-a[l]);else c[x][0]=INF;
        if(l>1&&l<n&&a[l]>=a[l-1]&&a[l]>=a[l+1])c[x][1]=1;
        if(l>1&&l<n&&a[l]<a[l-1]&&a[l]<a[l+1])pos[x]=l;else pos[x]=-1;
        return;
    }
    int Mid=l+r>>1;
    Build(x<<1,l,Mid);Build(x<<1|1,Mid+1,r);
    Up(x);
}
ll Query(int x,int l,int r,int L,int R,int y){
    if(l>R||r<L)return (y?(y==1?0:-1):INF);
    if(l>=L&&r<=R)return y==2?pos[x]:c[x][y];
    int Mid=l+r>>1;
    ll a=Query(x<<1,l,Mid,L,R,y),b=Query(x<<1|1,Mid+1,r,L,R,y);
    if(!y)return min(a,b);
    if(y==1)return max(a,b);
    return a!=-1?a:b;
}
void Update(int x,int l,int r,int y,int d,ll z){
    if(l==r){
        if(d==1)c[x][1]=z;else if(d==2)pos[x]=z;else c[x][0]=z;
        return;
    }
    int Mid=l+r>>1;
    if(y<=Mid)Update(x<<1,l,Mid,y,d,z);else Update(x<<1|1,Mid+1,r,y,d,z);
    Up(x);
}
inline ll Calc(int l,int r,int x){
    if(Query(1,1,n,l,r,1))return (x<<1);
    ll Ans=-INF;
    ll t=Query(1,1,n,l,r,0);
    if(t<INF)Ans=max(max(Ans,0ll),x-t<<1);
    ll T=Query(1,1,n,l,r,2);
    if(T!=-1)Ans=max(Ans,(x<d[T-1]?-x:x-(d[T-1]<<1))+(x<d[T]?-x:x-(d[T]<<1)));
    if(l==1)Ans=max(Ans,Abs(Query_node(1)+a[1]+x-Query_node(2)-a[2])-d[1]);
    if(r==n)Ans=max(Ans,Abs(Query_node(n)+a[n]+x-Query_node(n-1)-a[n-1])-d[n-1]);
    return Ans;
}
inline void Ch(int x){
    if(x>1&&x<n){
        ll t1=Query_node(x-1)+a[x-1],t2=Query_node(x)+a[x],t3=Query_node(x+1)+a[x+1];
        Update(1,1,n,x,1,(t2>=t1&&t2>=t3));
        Update(1,1,n,x,2,(t2<t1&&t2<t3?x:-1));
    }
    if(x<n){
        ll t2=Query_node(x)+a[x],t3=Query_node(x+1)+a[x+1];
        sum+=Abs(t2-t3)-d[x];d[x]=Abs(t2-t3);
        if(x>1&&!(t2<Query_node(x-1)+a[x-1]&&t2<t3))Update(1,1,n,x,0,max(t3-t2,x>1?Query_node(x-1)+a[x-1]-t2:0ll));else Update(1,1,n,x,0,INF);
    }
}
inline void Modify(int l,int r,int x){
    Update_node(l,x);Update_node(r+1,-x);
    Ch(l);if(l>1)Ch(l-1);
    if(l<r)Ch(r);if(r<n)Ch(r+1);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<n;i++)d[i]=Abs(a[i]-a[i+1]),sum+=d[i];
    Build(1,1,n);
    scanf("%d",&q);
    while(q--){
        scanf("%d%d%d%d",&k,&l,&r,&x);
        if(k==1)printf("%I64d\n",sum+Calc(l,r,x));else Modify(l,r,x);
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值