BZOJ 2388: 旅行规划 [分块 凸包 等差数列]

传送门

题意:

区间加和询问一段区间内整体前缀和的最大值


 

 

刚才还在想做完这道题做一道区间加等差数列结果发现这道就是....

唯一的不同在于前缀和一段区间加上等差数列后,区间后面也要加上一个常数!!!

线段树没法搞吧....分块!

每个块维护整体加标记,首项,公差

修改的时候:

左面不完整的块下放标记暴力重构;

中间的整块打标记;

右面不完整的块也是下放标记暴力重构,注意这个地方$r$之外的部分也要更新!

右面完整的块也要打标记!

 

怎么查询呢?

左右不完整的块暴力查询

中间的整块,可以发现我们每次修改的只有公差会造成影响,类似于斜率,凸包的形状不会改变(斜率的相对大小不会改变),所以维护一个凸包,查询时三分就行了

其实我现在还不会三分就去黄学长哪里抄了一个奇怪的东西

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=350;
const ll INF=1e18;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

int n,Q,op,x,y; ll a[N];
struct _blo{int l,r;}b[M];
int block,m,pos[N];
inline void ini(){
    block=sqrt(n); m=(n-1)/block+1;
    for(int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
    for(int i=1;i<=m;i++) b[i].l=(i-1)*block+1 , b[i].r=i*block;
    b[m].r=n;
}

struct ConvexHull{
    int a[M],n;
    int& operator [](int x){return a[x];}
}con[M];
int st[N];

struct Block{
    inline double slope(int i,int j){return (double)(a[i]-a[j])/(i-j);}
    void Set(int x){
        int top=0;
        for(int i=b[x].l ; i<=b[x].r ; i++){
            while(top>1 && slope(i,st[top-1])>=slope(st[top],st[top-1]) ) top--;
            st[++top]=i;
        }
        for(int i=1;i<=top;i++) con[x][i]=st[i]; 
        con[x].n=top; con[x][0]=con[x][top+1]=0;
    }

    ll f[M],d[M],add[M];
    void pushDown(int x){
        ll now=f[x];
        for(int i=b[x].l ; i<=b[x].r ; i++) a[i]+=now+add[x],now+=d[x];
        f[x]=d[x]=add[x]=0;
    }

    void Add(int l,int r,ll v){
        int pl=pos[l],pr=pos[r];
        ll now=0;
        if(pl==pr){
            pushDown(pl); 
            for(int i=l;i<=r;i++) now+=v,a[i]+=now;
            for(int i=r+1 ; i<=b[pr].r ; i++) a[i]+=now;
            Set(pl);

            for(int i=pr+1;i<=m;i++) add[i]+=now;
        }else{
            pushDown(pl);
            for(int i=l ; i<=b[pl].r ; i++) now+=v,a[i]+=now;
            Set(pl);

            for(int i=pl+1;i<pr;i++) now+=v,f[i]+=now,d[i]+=v,now+=v*(block-1);

            pushDown(pr); 
            for(int i=b[pr].l ; i<=r ; i++) now+=v,a[i]+=now;
            for(int i=r+1 ; i<=b[pr].r ;i++) a[i]+=now;
            Set(pr);
                
            for(int i=pr+1;i<=m;i++) add[i]+=now;
        }
    }

    inline ll cal(int x){ 
        if(x==0) return -INF;//!!!!!
        int t=pos[x]; 
        return a[x]+add[t] + f[t]+d[t]*(x-b[t].l) ; 
    }
    ll Bin(int x){
        int l=1,r=con[x].n;
        while(l<=r){
            int mid=(l+r)>>1;
            ll a=cal(con[x][mid-1]),b=cal(con[x][mid]),c=cal(con[x][mid+1]);
            if(a<b&&b<c) l=mid+1;
            else if(a>b&&b>c) r=mid-1;
            else return b;
        }
        return 0;
    }
    ll Que(int l,int r){
        int pl=pos[l],pr=pos[r];
        ll re=-INF;
        if(pl==pr)
            for(int i=l;i<=r;i++) re=max(re,cal(i));
        else{
            for(int i=l ; i<=b[pl].r ; i++) re=max(re,cal(i));
            for(int i=b[pr].l ; i<=r ; i++) re=max(re,cal(i));
            for(int i=pl+1;i<pr;i++) re=max(re,Bin(i));
        }
        return re;
    }
}B;

int main(){
    freopen("in","r",stdin);
    n=read(); ini();
    for(int i=1;i<=n;i++) a[i]=read()+a[i-1];
    for(int i=1;i<=m;i++) B.Set(i);
    Q=read();
    while(Q--){
        op=read();x=read();y=read();
        if(op==0) B.Add(x,y,read());
        else printf("%lld\n",B.Que(x,y));
    }
}

 

posted @ 2017-03-15 20:35  Candy?  阅读(500)  评论(0编辑  收藏  举报