【线段树】Fafa and Array

题意:

给出一个长为N的序列,需要支持两种操作:
1、求出在序列中某个位置加上x后,使得

i=1i<n|ai+1ai| ∑ i = 1 i < n | a i + 1 − a i |
尽量大,并求出最大值。
2、将区间 al,al+1,al+2ar a l , a l + 1 , a l + 2 … … a r 均加上x


分析:

这道题是用最优性来满足正确性的典型。

首先,我们考虑根据趋势,分为三种情况:
1.ai1aiai+1 1. a i − 1 ≤ a i ≤ a i + 1 1.ai+1aiai1 1. a i + 1 ≤ a i ≤ a i − 1
这种情况情况下,
如果 max(ai1,ai+1)aix m a x ( a i − 1 , a i + 1 ) − a i ≥ x ,那么在这个位置加x是不会对答案有影响的。
如果不然,那么答案就会加上: x+x2×(max(ai1,ai+1)ai) x + x − 2 × ( m a x ( a i − 1 , a i + 1 ) − a i ) 在这种情况下,答案一定会得到正影响。

2.ai1aiai+1 2. a i − 1 ≥ a i ≤ a i + 1
在这种情况下,如果要在这个点加x,是有可能为负数影响的,我们分析一下:
(ai1ai+1) ( 我 们 假 定 a i − 1 ≤ a i + 1 )

如果 ai+xai1,ai+1 a i + x ≤ a i − 1 , a i + 1
那么在这里加x,影响是 2×x − 2 × x ,为负

如果 ai1ai+xai+1 a i − 1 ≤ a i + x ≤ a i + 1
在这里加x,影响是:
xx+aiai+1(ai+1ai)=2×aiai+1 x − ( x + a i − a i + 1 − ( a i + 1 − a i ) ) = 2 × ( a i − a i + 1 ) 也为负。

如果 ai+xai1,ai+1 a i + x ≥ a i − 1 , a i + 1
那么在这里加x,影响是 2×x2×(ai+1ai)2×(ai1ai) 2 × x − 2 × ( a i + 1 − a i ) − 2 × ( a i − 1 − a i ) 正负均可能。

3.ai1,ai+1ai 3. a i − 1 , a i + 1 ≤ a i
在这种情况下,在这个点加x,那么影响为 2×x 2 × x ,始终为正且最大。

仔细观察我们可能为正的三个式子:
1. 2×x2×(max(ai1,ai+1)ai) 1.   2 × x − 2 × ( m a x ( a i − 1 , a i + 1 ) − a i )
2. 2×x2×(ai+1ai)2×(ai1ai) 2.   2 × x − 2 × ( a i + 1 − a i ) − 2 × ( a i − 1 − a i )
3. 2×x 3.   2 × x
很容易总结出,这三个式子都可以表示为:
2×xmax(0,ai1ai)max(0,ai+1ai) 2 × x − m a x ( 0 , a i − 1 − a i ) − m a x ( 0 , a i + 1 − a i )
这样一来,我们可以通过维护 max(0,ai1ai)+max(0,ai+1ai) m a x ( 0 , a i − 1 − a i ) + m a x ( 0 , a i + 1 − a i ) 的最小值,来得到对答案的影响。

有的小朋友就会问了,那么影响为负的情况怎么办呢?那两种式子不能表示为这种形式啊?
其实很容易发现,2类情况不可能充满一个大于1的区间,即:一个大于1的区间不可能全为波谷。
又因为另外两种情况答案均不为负,所以我们可以保证在大于1的区间内,一定不会造成负影响。(这里就是我个人认为这道题最巧妙的一点,虽然其本身很显然,但这种用最优性来保证正确性的思考方式很难得一见)

那么我们只需要对区间大小分类讨论:若区间大小为1,就只能在那个点加x,手动得到答案。
如区间大小不为1,就可以按照我们维护的值来求答案。

差点忘了还有修改操作:
很容易发现,其实如果在一个区间内均加上一个值,那么区间内部的差是不会影响的,因此我们只需要考虑两个端点即可,说白了这里的修改其实只是单点修改。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 100010
#define INF 1e18
using namespace std;
long long tree[MAXN*4],ans;
int n,m;
long long a[MAXN];
long long absx(long long x){
    return x>0?x:-x;
}
void build(int id,int l,int r){
    if(l==r){
        ans+=absx(a[l]);
        if(l!=1)
            tree[id]=max(0ll,-a[l-1])+max(0ll,a[l]);
        return ;
    }
    int mid=(l+r)>>1;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    tree[id]=min(tree[id<<1],tree[id<<1|1]);
    //PF("[%d %d %lld]",l,r,tree[id]);
}
void add(int id,int l,int r,int pos,long long x){
    if(l==r){
        ans-=absx(a[l]);
        a[l]+=x;
        ans+=absx(a[l]);
        //PF("[%lld]",ans);
        if(pos>1)
            tree[id]=max(0ll,-a[l-1])+max(0ll,a[l]);
        if(x!=0&&pos<n-1)
            add(1,1,n-1,pos+1,0);
        return ;
    }
    int mid=(l+r)>>1;
    if(pos<=mid)
        add(id<<1,l,mid,pos,x);
    else
        add(id<<1|1,mid+1,r,pos,x);
    tree[id]=min(tree[id<<1],tree[id<<1|1]);
}
long long query(int id,int l,int r,int l1,int r1){
    if(l>=l1&&r<=r1)
        return tree[id];
    int mid=(l+r)>>1;
    long long res=INF;
    if(l1<=mid)
        res=min(res,query(id<<1,l,mid,l1,r1));
    if(r1>mid)
        res=min(res,query(id<<1|1,mid+1,r,l1,r1));
    return res;
}
int main(){
    int tag,l,r;
    long long x;
    SF("%d",&n);
    for(int i=1;i<=n;i++)
        SF("%d",&a[i]);
    for(int i=1;i<n;i++)
        a[i]=a[i+1]-a[i];
    build(1,1,n-1);
    SF("%d",&m);
    for(int i=1;i<=m;i++){
        SF("%d%d%d%lld",&tag,&l,&r,&x);
        if(tag==1){
            if(l==r){
                if(l==1)
                    PF("%lld\n",ans-absx(a[1])+absx(a[1]-x));
                else if(l==n)
                    PF("%lld\n",ans-absx(a[n-1])+absx(a[n-1]+x));
                else
                    PF("%lld\n",ans-absx(a[l-1])+absx(a[l-1]+x)-absx(a[l])+absx(a[l]-x));
            }
            else{
                long long res=max(0ll,2*x-2*query(1,1,n-1,max(2,l),r));
                //PF("[%lld]",2*x-2*query(1,1,n-1,max(2,l),r));
                if(l==1)
                    res=max(res,-absx(a[1])+absx(a[1]-x));
                if(r==n)
                    res=max(res,-absx(a[n-1])+absx(a[n-1]+x));
                PF("%lld\n",ans+res);
            }
        }
        else{
            if(l!=1)
                add(1,1,n-1,l-1,x);
            if(r!=n)
                add(1,1,n-1,r,-x);
        }
        //PF("[%lld]",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值