BZOJ2388: 旅行规划

http://www.lydsy.com/JudgeOnline/problem.php?id=2388

  带区间加修改,求区间内的最大前缀和。

  设sum[i]代表i的前缀和,tag[i][j]代表i到j位置上每个数字都加上了tag[i][j],add[i][j]代表i到j位置上每个位置的前缀和都加上了add[i][j]。

  对于区间[l,r],设最大答案为ans,ans=max{add[l][r]+tag[l][r]×(i-l+1)+sum[i]|l≤i≤r}。

  设x=(i-l+1),y=sum[i],则有ans=add[l][r]+tag[l][r]×x+y,可以化简为y=-tag[l][r]×x+ans-add[l][r],就是给定一些点,要最大化过某个点且斜率给定的直线的截距,维护上凸壳即可。

  所以我们分块,对于每一块维护上凸壳,查询操作O(√nlog(n)),修改O(√n)。

  注意重建某一块的上凸壳之前要标记下传。

#include<bits/stdc++.h>
const int maxn=100015,maxb=415;
typedef long long int64;
using namespace std;
int n,m,num,siz,idx[maxn];
int64 sum[maxn];
struct Tblock{
    static const int maxsiz=415;
    static const double eps=1e-9;
    int l,r,top;int64 add,tag;
    struct Tpoint{int64 x,y;}stk[maxsiz];
    void modify(int64 _add,int64 _tag){add+=_add;tag+=_tag;}
    double slope(Tpoint a,Tpoint b){return 1.0*(b.y-a.y)/(b.x-a.x);}
    void push_down(){for (int i=l;i<=r;++i) sum[i]+=tag*(i-l+1)+add;tag=add=0;}
    void rebuild(){
        top=0;
        for (int i=l;i<=r;++i){
            Tpoint cur=(Tpoint){i-l+1,sum[i]};
            while (top>1&&slope(stk[top-1],stk[top])+eps<slope(stk[top],cur)) --top;
            stk[++top]=cur;
        }
        stk[0]=(Tpoint){stk[1].x-1,(int64)-1e18};
        stk[top+1]=(Tpoint){stk[top].x+1,(int64)-1e18};
    }
    int64 query(){
        double k=-tag;int l=1,r=top;
        while (1){
            int mid=(l+r)>>1;
            double lk=slope(stk[mid-1],stk[mid]),rk=slope(stk[mid],stk[mid+1]);
            if (lk+eps>=k&&rk<=eps+k) return stk[mid].y+tag*stk[mid].x+add;
            else if (rk>eps+k) l=mid+1;else r=mid-1;
        }
    }
}block[maxb];
void init_block(){
    siz=sqrt(n);
    for (int j,i=1;i<=n;i=j){
        for (++num,j=i;j-i+1<=siz&&j<=n;++j) idx[j]=num;
        block[num].l=i;block[num].r=j-1;block[num].rebuild();
    }
}
void init(){
    scanf("%d",&n);
    for (int i=1;i<=n;++i){scanf("%lld",&sum[i]);sum[i]+=sum[i-1];}
    init_block();
}
void modify(int l,int r,int64 v){
    if (idx[l]==idx[r]){
        block[idx[l]].push_down();
        for (int i=l;i<=r;++i) sum[i]+=v*(i-l+1);
        for (int i=r+1;i<=block[idx[r]].r;++i) sum[i]+=v*(r-l+1);
        block[idx[l]].rebuild();
        for (int i=idx[l]+1;i<=num;++i) block[i].modify(v*(r-l+1),0);
    }
    else{
        block[idx[l]].push_down();block[idx[r]].push_down();
        for (int i=l;i<=block[idx[l]].r;++i) sum[i]+=v*(i-l+1);
        for (int i=block[idx[r]].l;i<=r;++i) sum[i]+=v*(i-l+1);
        for (int i=r+1;i<=block[idx[r]].r;++i) sum[i]+=v*(r-l+1);
        block[idx[l]].rebuild();block[idx[r]].rebuild();
        for (int i=idx[l]+1;i<=idx[r]-1;++i) block[i].modify(v*(block[idx[l]].r-l+1+siz*(i-idx[l]-1)),v);
        for (int i=idx[r]+1;i<=num;++i) block[i].modify(v*(r-l+1),0);
    }
}
int64 query(int l,int r){
    int64 res=-1e18;
    if (idx[l]==idx[r])
        for (int i=l;i<=r;++i) res=max(res,sum[i]+block[idx[i]].add+block[idx[i]].tag*(i-block[idx[i]].l+1));
    else{
        for (int i=l;i<=block[idx[l]].r;++i) res=max(res,sum[i]+block[idx[i]].add+block[idx[i]].tag*(i-block[idx[i]].l+1));
        for (int i=block[idx[r]].l;i<=r;++i) res=max(res,sum[i]+block[idx[i]].add+block[idx[i]].tag*(i-block[idx[i]].l+1));
        for (int i=idx[l]+1;i<=idx[r]-1;++i) res=max(res,block[i].query());
    }
    return res;
}
void work(){
    scanf("%d",&m);
    for (int i=1;i<=m;++i){
        int t,l,r;int64 v;
        scanf("%d%d%d",&t,&l,&r);
        switch (t){
            case 0:scanf("%lld",&v);modify(l,r,v);break;
            case 1:printf("%lld\n",query(l,r));break;
        }
    }
}
int main(){
    init();
    work();
    return 0; 
}
my code

 

posted @ 2015-08-11 09:33  iamCYY  阅读(575)  评论(0编辑  收藏  举报