bzoj 5089: 最大连续子段和

来源:互联网 发布:淘宝汽车商城 编辑:程序博客网 时间:2024/05/20 12:22

前言

似乎这题有O(nn)的做法,但是我并没有想到。。
一个n5/3的做法就想死我了,好吧。。
第一次搞用凸包维护这些东西的题。。以前都没有写过

题解

我们发现,如果我们把一个区间,他的数字个数当做x,和当做y,把他看做坐标系上的点
然后对于加上k后的最大值,其实就是你画一条斜率为-k的线,从y轴的无限大掉下来,第一个碰到的点,在y轴上的截距就是最大值
我们可以发现,k明显是递增的,因为加上的数>0
我们就可以用一个上凸的凸包来维护这些东西
由于斜率递增,最优点肯定是单调向右移的
就可以搞了
对于修改操作,就采取暴力重构的方法,所以吧块的大小设为n1/3
就可以了
对于询问操作,就用正常的合并方式就可以了
维护左边右边的最大值维护类似
CODE:(巨长巨慢空间巨大)

#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>using namespace std;typedef long long LL;const LL N=50005;const LL MAX=(1LL<<55);const LL size=40;inline LL read(){    LL x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}LL n,m;LL a[N];LL belong[N];struct P{LL x,y;};double xielv (P x,P y){return (double)(x.y-y.y)/(double)(x.x-y.x);}struct tb{    LL top,o;    P a[size+5];    void Ins (P x)//现在假如了这个点     {        while (top>1&&xielv(x,a[top-1])>=xielv(a[top],a[top-1])) top--;        a[++top]=x;    }    void move (LL x)//斜率直线移动     {        while (o<top&&xielv(a[o],a[o+1])>=x) o++;    }};struct qq{    LL l,r,k;//范围   这个块加了多少     LL num;    tb s[3];    void rebuild ()    {        num=0;        for (LL u=l;u<=r;u++) a[u]+=k,num+=a[u];        LL n=r-l+1;k=0;        s[0].o=s[1].o=s[2].o=1;        s[0].top=s[1].top=s[2].top=0;        for (LL u=1;u<=n;u++)        {            LL lalal=0;            for (LL i=1;i<=u;i++) lalal=lalal+a[l+i-1];            P x=(P){u,lalal};            for (LL i=u+1;i<=n;i++)//右端点在哪里            {                lalal=lalal-a[i-u+l-1];                lalal=lalal+a[i+l-1];                x.y=max(x.y,lalal);            }            //printf("shen:%lld %lld\n",x.x,x.y);            s[0].Ins(x);        }        P x=(P){0,0};        for (LL u=1;u<=n;u++)        {            x.y+=a[l+u-1];x.x=u;            s[1].Ins(x);        }        x=(P){0,0};        for (LL u=n;u>=1;u--)        {            x.y+=a[l+u-1];x.x=n-u+1;            s[2].Ins(x);        }        s[0].move(0);s[1].move(0);s[2].move(0);        //printf("OZY:%lld\n",s[0].top);    }    void add (LL x)//区间加     {        num=num+x*(r-l+1);        k+=x;        s[0].move(-k);s[1].move(-k);s[2].move(-k);    }    LL get (LL x)    {        P o=s[x].a[s[x].o];        return o.y+o.x*k;    }}g[N];void print(){    for (int u=1;u<=n;u++)  printf("%lld ",a[u]);    printf("\n");    for (LL u=1;u<=n;u++)        printf("%lld ",belong[u]);    printf("\n");    for (LL u=1;u<=belong[n];u++)        printf("%lld L:%lld R:%lld num:%lld 0:%lld 1:%lld 2:%lld\n",u,g[u].l,g[u].r,g[u].num,g[u].get(0),g[u].get(1),g[u].get(2));    printf("\n");}int main(){    n=read();m=read();    for (LL u=1;u<=n;u++)   a[u]=read();    for (LL u=1;u<=n;u++)     {        belong[u]=(u-1)/size+1;         g[belong[u]].r=u;    }    for (LL u=1;u<=belong[n];u++)    {        g[u].l=(u-1)*size+1;        g[u].k=0;        g[u].rebuild();    }//  print();    while (m--)    {        char ss[5];        scanf("%s",ss);        if (ss[0]=='Q')//询问        {            LL l,r;            l=read();r=read();            LL ans=0,lalal=0;            if (belong[l]==belong[r])//在一个块里面            {                for (LL u=l;u<=r;u++)                {                    lalal=lalal+a[u]+g[belong[l]].k;                    if (lalal<0) lalal=0;                    ans=max(ans,lalal);                }                printf("%lld\n",ans);            }            else//不在一个块里面             {                LL ans=0,lalal=0;                for (LL u=l;u<=g[belong[l]].r;u++)                {                    lalal=lalal+a[u]+g[belong[l]].k;                    if (lalal<0) lalal=0;                    ans=max(ans,lalal);                }                for (LL u=belong[l]+1;u<belong[r];u++)                {                    ans=max(ans,g[u].get(0));                    ans=max(ans,g[u].get(1)+lalal);                    lalal=max(lalal+g[u].num,g[u].get(2));                    if (lalal<0) lalal=0;                }                for (LL u=g[belong[r]].l;u<=r;u++)                {                    lalal=lalal+a[u]+g[belong[r]].k;                    if (lalal<0) lalal=0;                    ans=max(ans,lalal);                }                printf("%lld\n",ans);            }        }        else        {            LL l,r,x;            l=read();r=read();x=read();            if (belong[l]==belong[r])//在这一个块里面             {                for (LL u=l;u<=r;u++) a[u]+=x;                g[belong[l]].rebuild();            }            else            {                for (LL u=l;u<=g[belong[l]].r;u++) a[u]+=x;                g[belong[l]].rebuild();                for (LL u=belong[l]+1;u<belong[r];u++)  g[u].add(x);                for (LL u=g[belong[r]].l;u<=r;u++) a[u]+=x;                g[belong[r]].rebuild();            }        }        //print();    }    return 0;}