Codeforce F. Fafa and Array

题意

这里

题解

题目让我们求差分和。。
那么我们就直接先差分就可以了
然后差分完,和就是和了。。
然后对于2这个操作,就相当于是两个单点修改
然后一操作,其实就相当于让两个相邻数,假设这两个是 (a,b) ( a , b )
那么其实就是把他们变成 (ax,b+x) ( a − x , b + x )
然后你考虑一下,改掉他们以后,对答案的贡献就是
|ax|+|b+x||a||b| | a − x | + | b + x | − | a | − | b |

然后看了一下大众的做法,都是发现一个结论
那么就是,如果存在有 a<0b>0 a < 0 , b > 0 ,那么肯定是修改这里是最优的
否则,我们可以就要讨论三种情况,但是注意到,如果没有 a<0,b>0 a < 0 , b > 0 的话,那么 a>0,b<0 a > 0 , b < 0 的情况最多只有一个位置
然后你就找到这个点,暴力询问
然后另外两种情况,可以稍微推一下式子就可以了

但是我觉得这种方法过于麻烦。。要写很多东西,虽然代码都不长。。但是肯定没有我的方法优越啊【自信】
你考虑一下,我们可以对上面这个绝对值暴力展开,获得四种情况
我们分别讨论四种情况可以取到的最大值就可以了
具体来说
你可以把上面这个式子展开成
ax+b+x|a||b|=a+b|a||b| a − x + b + x − | a | − | b | = a + b − | a | − | b |
然后这个式子的最大值,就是这一段里面 a+b|a||b| a + b − | a | − | b | 的最大值了
然后其他也是一样
可能你会问,如果绝对值展开错了怎么办?
其实我们完全不用担心绝对值展开错了的情况
因为他要的是最大值,所以绝对值展开错了肯定是没有展开对的情况优啊

然后这题就变成是一个水题了。。

CODE:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const LL MAX=(1<<30);
const LL N=100005;
LL n,q;
struct qq
{
    LL l,r;
    LL s1,s2;
    LL c,sum;//这个点的值   绝对值的总和
    LL a[4];
}tr[N*2];LL num;
LL a[N];
void renovate (LL *a,LL x,LL y)
{
    a[0]=max(a[0],x+y-abs(x)-abs(y));
    a[1]=max(a[1],x-y-abs(x)-abs(y));
    a[2]=max(a[2],y-x-abs(x)-abs(y));
    a[3]=max(a[3],-x-y-abs(x)-abs(y));
} 
void update (LL now)
{
    LL s1=tr[now].s1,s2=tr[now].s2;
    LL x=a[tr[s1].r],y=a[tr[s2].l];
    tr[now].sum=tr[s1].sum+tr[s2].sum;
    for (LL u=0;u<4;u++)    tr[now].a[u]=max(tr[s1].a[u],tr[s2].a[u]);
    renovate(tr[now].a,x,y);
}
void bt (LL l,LL r)
{
    LL  A=++num;
    tr[A].l=l;tr[A].r=r;
    if (l==r)   
    {
        tr[A].c=a[l];
        tr[A].sum=abs(a[l]);
        memset(tr[A].a,-63,sizeof(tr[A].a));
        return ;
    }
    LL mid=(l+r)>>1;
    tr[A].s1=num+1;bt(l,mid);
    tr[A].s2=num+1;bt(mid+1,r);
    update(A);
}
void change (LL now,LL x)
{
    if (tr[now].l==tr[now].r)   {tr[now].c=a[tr[now].l];tr[now].sum=abs(a[tr[now].l]);return ;}
    LL mid=(tr[now].l+tr[now].r)>>1;
    LL s1=tr[now].s1,s2=tr[now].s2;
    if (x<=mid) change(s1,x);
    else change(s2,x);
    update(now);
}
LL ans[4];
void get (LL now,LL l,LL r)
{
    if (tr[now].l==l&&tr[now].r==r)
    {
        for (LL u=0;u<4;u++)    ans[u]=max(ans[u],tr[now].a[u]);
        return ;
    }
    LL s1=tr[now].s1,s2=tr[now].s2;
    LL mid=(tr[now].l+tr[now].r)>>1;
    if (r<=mid) get(s1,l,r);
    else if (l>mid) get(s2,l,r);
    else
    {
        get(s1,l,mid);
        get(s2,mid+1,r);
        LL x=a[tr[s1].r],y=a[tr[s2].l];
        renovate(ans,x,y);
    }
}
int main()
{
    scanf("%I64d",&n);
    for (LL u=1;u<=n;u++)   scanf("%I64d",&a[u]);
    n--;for (LL u=1;u<=n;u++)   a[u]=a[u]-a[u+1];
    bt(1,n);
    scanf("%I64d",&q);
    for (LL u=1;u<=q;u++)
    {
        LL op,l,r,x;
        scanf("%I64d%I64d%I64d%I64d",&op,&l,&r,&x);
        if (op==2)//区间修改 
        {
            if (l!=1) 
            {
                a[l-1]-=x;
                change(1,l-1);
            }
            if (r<=n)   {a[r]+=x;change(1,r);}
        }
        else
        {
            memset(ans,-63,sizeof(ans));
            get(1,max(1LL,l-1),min(n,r));
            LL lalal=ans[0];
            lalal=max(lalal,ans[1]-2*x);
            lalal=max(lalal,ans[2]+2*x);
            lalal=max(lalal,ans[3]);
            /*for (LL j=0;j<4;j++) printf("%I64d ",ans[j]);*/
            printf("%I64d\n",tr[1].sum+lalal);
        }
        /*for (LL i=1;i<=num;i++)
        {
            printf("l:%I64d r:%I64d c:%I64d sum:%I64d\n",tr[i].l,tr[i].r,tr[i].c,tr[i].sum);
            for (LL j=0;j<4;j++) printf("%I64d ",tr[i].a[j]);
            printf("\n");
        }*/
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值