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; }