Codeforces 935 F. Fafa and Array —— 线段树

This way

题意:

给你一个长度为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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值