BZOJ5089 最大连续子段和(分块)

  假设所有操作都是对整个序列的。考虑每个子区间,区间和与其被加的值构成一次函数关系。最大子段和相当于多个子区间取最大值,答案显然就在这些一次函数构成的下凸壳上。如果预处理出凸壳,只要在凸壳上暴力跳就可以回答询问了,因为加的都是正数,并且斜率不同的一次函数数量是O(n)的。暴力建凸壳的复杂度是O(n2)的。

  那么考虑分块。每个块预处理出凸壳。区间加时,对于整块在凸壳上暴跳,零散部分暴力重构。查询时合并区间,类似于单点加的线段树做法,维护块内最大前缀和、最大后缀和。块大小取n1/3时最优。

  造凸壳写挂调了1h,退役了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 50010
#define NUM 2000
#define BLOCK 100
#define inf 10000000000000000ll
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,m,pos[N],num,block;
ll a[N],tmp[N],PRE[N],SUF[N];
struct line
{
    ll k,b;
    ll f(ll x){return k*x+b;}
};
ll cross(line x,line y){return (x.b-y.b-1)/(y.k-x.k)+1;}
struct hull
{
    line a[BLOCK];int cnt,cur;
    void ins(line x){while (cnt&&x.b>=a[cnt].b||cnt>1&&x.f(cross(a[cnt],a[cnt-1]))>a[cnt].f(cross(a[cnt],a[cnt-1]))) cnt--;a[++cnt]=x;}
    void clear(){cnt=0,cur=1,ins((line){0,0});}
    void jump(ll x){while (cur<cnt&&a[cur].f(x)<a[cur+1].f(x)) cur++;}
    ll get(ll x){return a[cur].f(x);}
};
struct data
{
    hull pre,suf,seq;int L,R;ll lazy,sum;
    void build()
    {
        for (int i=L;i<=R;i++) a[i]+=lazy;lazy=0;
        sum=0;for (int i=L;i<=R;i++) sum+=a[i];
        ll x=0;pre.clear();
        for (int i=L;i<=R;i++) pre.ins((line){i-L+1,x+=a[i]});
        x=0;suf.clear();
        for (int i=R;i>=L;i--) suf.ins((line){R-i+1,x+=a[i]});
        seq.clear();
        for (int k=1;k<=R-L+1;k++)
        {
            ll s=-inf;x=0;for (int i=L;i<=L+k-1;i++) x+=a[i];
            for (int i=L+k-1;i<=R;i++) s=max(s,x),x+=a[i+1]-a[i-k+1];
            seq.ins((line){k,s});
        }
    }
    void add(int x){sum+=1ll*(R-L+1)*x,lazy+=x,pre.jump(lazy),suf.jump(lazy),seq.jump(lazy);}
    ll maxpre(){return pre.get(lazy);}
    ll maxsuf(){return suf.get(lazy);}
    ll maxseq(){return seq.get(lazy);}
}f[NUM];
ll getseq(int l,int r)
{
    ll s=0,ans=0;
    for (int i=l;i<=r;i++)
    {
        s+=tmp[i];
        if (s<0) s=0;
        ans=max(ans,s);
    }
    return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj5089.in","r",stdin);
    freopen("bzoj5089.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read(),m=read();block=2*pow(n,1.0/3)+1;num=(n-1)/block+1;
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<=num;i++)
    {
        f[i].L=f[i-1].R+1,f[i].R=min(n,f[i].L+block-1);
        for (int j=f[i].L;j<=f[i].R;j++) pos[j]=i;
        f[i].build();
    }
    while (m--)
    {
        char c=getc();
        if (c=='A')
        {
            int l=read(),r=read(),x=read();
            if (pos[l]==pos[r])
            {
                for (int i=l;i<=r;i++) a[i]+=x;
                f[pos[l]].build();
            }
            else
            {
                for (int i=pos[l]+1;i<pos[r];i++) f[i].add(x);
                for (int i=l;i<=f[pos[l]].R;i++) a[i]+=x;f[pos[l]].build();
                for (int i=f[pos[r]].L;i<=r;i++) a[i]+=x;f[pos[r]].build();
            }
        }
        else
        {
            int l=read(),r=read();
            if (pos[l]==pos[r])
            {
                for (int i=l;i<=r;i++) tmp[i]=a[i]+f[pos[l]].lazy;
                printf(LL,getseq(l,r));
            }
            else
            {
                for (int i=l;i<=f[pos[l]].R;i++) tmp[i]=a[i]+f[pos[l]].lazy;
                for (int i=f[pos[r]].L;i<=r;i++) tmp[i]=a[i]+f[pos[r]].lazy;
                ll ans=max(getseq(l,f[pos[l]].R),getseq(f[pos[r]].L,r)),s;
                for (int i=pos[l]+1;i<pos[r];i++) ans=max(ans,f[i].maxseq());
                for (int i=pos[l];i<=pos[r];i++) PRE[i]=SUF[i]=0;
                s=0;for (int i=f[pos[l]].R;i>=l;i--) s+=tmp[i],SUF[pos[l]]=max(SUF[pos[l]],s);
                s=0;for (int i=f[pos[r]].L;i<=r;i++) s+=tmp[i],PRE[pos[r]]=max(PRE[pos[r]],s);
                for (int i=pos[l]+1;i<pos[r];i++) SUF[i]=max(f[i].maxsuf(),SUF[i-1]+f[i].sum);
                for (int i=pos[r]-1;i>pos[l];i--) PRE[i]=max(f[i].maxpre(),PRE[i+1]+f[i].sum);
                for (int i=pos[l];i<pos[r];i++) ans=max(ans,SUF[i]+PRE[i+1]);
                printf(LL,ans);
            }
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/Gloid/p/10073408.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值