dtoj#4212. 小X爱旅行(travel)

题目描述:

OIVillage 是一个风景秀美的乡村,为了更好的利用当地的旅游资源,吸引游客,推动经济发展,xkszltl 决定修建了一条铁路将当地 $n$ 个最著名的经典连接起来,让游客可以通过火车从铁路起点( $1$ 号景点)出发,依次游览每个景区。为了更好的评价这条铁路,xkszltl 为每一个景区都赋予了一个美观度,而一条旅行路径的价值就是它所经过的景区的美观度之和。不过,随着天气与季节的变化,某些景点的美观度也会发生变化。

xkszltl 希望为每位旅客提供最佳的旅行指导,但是由于游客的时间有限,不一定能游览全部景区,然而他们也不希望旅途过于短暂,所以每个游客都希望能在某一个区间内的车站结束旅程,而 xkszltl 的任务就是为他们选择一个终点使得旅行线路的价值最大。可是当地的景点与前来观光的旅客实在是太多了,xkszltl 无法及时完成任务,于是找到了准备虐杀 NOI2019 的你,希望你能帮助他完成这个艰巨的任务。

输入:

第一行给出一个整数 $n$,接下来一行给出 $n$ 的景区的初始美观度。

第三行给出一个整数 $m$,接下来 $m$ 行每行为一条指令:

$1.~~~0~x~y~k$:表示将 $x$ 到 $y$ 这段铁路边上的景区的美观度加上 $k$;

$2.~~~1~x~y$:表示有一名旅客想要在 $x$ 到 $y$ 这段(含 $x$ 与 $y$ )中的某一站下车,你需要告诉他最大的旅行价值。

数据范围:

对于 $20\%$ 的数据,$n,m≤3000$;

对于 $40\%$ 的数据,$n,m≤30000$;

对于 $50\%$ 的数据,$n,m≤50000$;

另外 $20\%$ 的数据,$n,m≤100000$,修改操作 $≤20$;

对于 $100\%$ 的数据,$n,m≤100000$。

算法标签:分块,凸包

思路:

考虑分块计算,对于每一个块做一个凸包,对于单词修改或询问如果遍布整个块,就对这个块记录一个 $k,b$ 表示整个块的数都要进行的操作。

因为是一次函数所以更改后最优答案一定在凸包上。

对于只修改或查询一个块内一部分点的情况,暴力操作。

以下代码:

#include<bits/stdc++.h>
#define il inline
#define db double
#define LL long long
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1e5+5,M=1505;
const LL inf=1e18;
vector<int> v[M];
int n,m,sz,gr[N],sta[N],top;
LL sum[N],k[N],b[N];
il int read(){
    int x,f=1;char ch;
    _(!)ch=='-'?f=-1:f;x=ch^48;
    _()x=(x<<1)+(x<<3)+(ch^48);
    return f*x;
}
il db getk(int x,int y){
    return (db)(sum[x]-sum[y])/(db)(x-y);
}
il void build(int x){
    int l=(x-1)*sz+1,r=min(x*sz,n);
    sta[top=1]=l;
    for(int i=l+1;i<=r;i++){
        while(top>1&&getk(sta[top],sta[top-1])<getk(sta[top-1],i))top--;
        sta[++top]=i;
    }
    v[x].resize(top+1);
    for(int i=1;i<=top;i++)v[x][i]=sta[i];
}
il LL cal(int x){
    return sum[x]+k[gr[x]]*x+b[gr[x]];
}
il LL query(int x){
    int l=1,r=v[x].size()-1;
    if(l>r)return cal(v[x][1]);
    while(l<r){
        int mid=(l+r)>>1;
        if(cal(v[x][mid])<cal(v[x][mid+1]))l=mid+1;
        else r=mid;
    }
    return cal(v[x][l]);
}
il void update(int x){
    int l=(x-1)*sz+1,r=min(x*sz,n);
    LL v=k[x]*l+b[x];
    for(int i=l;i<=r;i++)sum[i]+=v,v+=k[x];
    k[x]=b[x]=0;
}
int main()
{
    n=read();sz=sqrt(n/3);
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+read();
    for(int i=1;i<=n;i++)gr[i]=(i-1)/sz+1;
    for(int i=1;i<=gr[n];i++)build(i);
    m=read();
    while(m--){
        int op=read(),x=read(),y=read();
        if(op){
            LL ans=-inf;
            if(gr[x]^gr[y]){
                for(int i=x;i<=gr[x]*sz;i++)ans=max(ans,cal(i));
                for(int i=(gr[y]-1)*sz+1;i<=y;i++)ans=max(ans,cal(i));
                for(int i=gr[x]+1;i<gr[y];i++)ans=max(ans,query(i));
            }
            else{
                for(int i=x;i<=y;i++)ans=max(ans,cal(i));
            }
            printf("%lld\n",ans);
        }
        else{
            int c=read();
            if(gr[x]^gr[y]){
                LL val=0;
                for(int i=x;i<=gr[x]*sz;i++)val+=c,sum[i]+=val;
                val=1ll*((gr[y]-1)*sz-x+1)*c;
                for(int i=(gr[y]-1)*sz+1;i<=y;i++)val+=c,sum[i]+=val;
                for(int i=y+1;i<=min(gr[y]*sz,n);i++)sum[i]+=val;
                for(int i=gr[y]+1;i<=gr[n];i++)b[i]+=val;
                update(gr[x]);update(gr[y]);build(gr[x]);build(gr[y]);
                val=-1ll*c*(x-1);
                for(int i=gr[x]+1;i<gr[y];i++)k[i]+=c,b[i]+=val;
            }
            else{
                LL val=0;
                for(int i=x;i<=y;i++)val+=c,sum[i]+=val;
                for(int i=y+1;i<=min(gr[y]*sz,n);i++)sum[i]+=val;
                for(int i=gr[y]+1;i<=gr[n];i++)b[i]+=val;
                update(gr[x]);build(gr[x]);
            }
        }
    }
    return 0;
}
View Code

 

posted @ 2019-02-27 13:26  Jessiejzy  阅读(286)  评论(0编辑  收藏  举报