Description
OIVillage是一个风景秀美的乡村,为了更好的利用当地的旅游资源,吸引游客,推动经济发展,xkszltl决定修建了一条铁路将当地n个最著名的经典连接起来,让游客可以通过火车从铁路起点(1号景点)出发,依次游览每个景区。为了更好的评价这条铁路,xkszltl为每一个景区都哦赋予了一个美观度,而一条旅行路径的价值就是它所经过的景区的美观度之和。不过,随着天气与季节的变化,某些景点的美观度也会发生变化。
xkszltl希望为每位旅客提供最佳的旅行指导,但是由于游客的时间有限,不一定能游览全部景区,然而他们也不希望旅途过于短暂,所以每个游客都希望能在某一个区间内的车站结束旅程,而xkszltl的任务就是为他们选择一个终点使得旅行线路的价值最大。可是当地的景点与前来观光的旅客实在是太多了,xkszltl无法及时完成任务,于是找到了准备虐杀NOI2011的你,希望你能帮助他完成这个艰巨的任务。
Input
第一行给出一个整数n,接下来一行给出n的景区的初始美观度。
第三行给出一个整数m,接下来m行每行为一条指令:
1. 0 x y k:表示将x到y这段铁路边上的景区的美观度加上k;
2. 1 x y:表示有一名旅客想要在x到y这段(含x与y)中的某一站下车,你需要告诉他最大的旅行价值。
Output
对于每个询问,输出一个整数表示最大的旅行价值。
Sample Input
5
1 8 -8 3 -7
3
1 1 5
0 1 3 6
1 2 4
Sample Output
9
22
HINT
Data Limit:
对于20%的数据,n,m≤3000;
对于40%的数据,n,m≤30000;
对于50%的数据,n,m≤50000;
另外20%的数据,n,m≤100000,修改操作≤20;
对于100%的数据,n,m≤100000。
解题思路:
相当于要支持区间加法,动态维护前缀和。
如果我们把下标看做横坐标,前缀和看做纵坐标,那答案肯定是在凸包的最高点上,所以关键在于动态维护凸包。
本来先用线段树,但修改时无法快速合并,所以还是上分块大法。
分块维护块内凸包,注意到一次修改增加k,如果覆盖了整块,块内相邻两点的斜率都会增加k,所以凸包上的点是不会变的,而r之后的块都加了固定的值,只有询问边缘两块会受影响,暴力重构即可。所以每块要打三个标记,斜率变化的首项fir,斜率变化的公差d,整块加的值add,重构时pushdown即可。
每次询问二分整块的凸包,直接计算边缘的点值。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=100005,M=350;
const ll INF=1e18;
int n,m,cnt,S;
int belong[N],st[M],ed[M],num[M];
int stk[M],top,p[M][M];
ll a[N],fir[M],d[M],add[M];
inline double slope(int x,int y){return (double)(a[y]-a[x])/(y-x);}
inline ll calc(int x)
{
if(x==0||x==n+1)return -INF;
int t=belong[x];
return a[x]+fir[t]+d[t]*(x-st[t])+add[t];
}
void build(int x)
{
stk[top=1]=st[x];
for(int i=st[x]+1;i<=ed[x];i++)
{
while(top>=2&&slope(stk[top-1],stk[top])<slope(stk[top-1],i))top--;
stk[++top]=i;
}
stk[0]=0,stk[top+1]=n+1;
num[x]=top;
for(int i=0;i<=top+1;i++)p[x][i]=stk[i];
}
void pushdown(int x)
{
ll tmp=fir[x];
for(int i=st[x];i<=ed[x];i++)
a[i]+=tmp,tmp+=d[x],a[i]+=add[x];
fir[x]=d[x]=add[x]=0;
}
ll query(int x)
{
int l=1,r=num[x];
while(l<=r)
{
int mid=l+r>>1;
ll t1=calc(p[x][mid-1]),t2=calc(p[x][mid]),t3=calc(p[x][mid+1]);
if(t1<t2&&t2<t3)l=mid+1;
else if(t1>t2&&t2>t3)r=mid-1;
else return t2;
}
}
int main()
{
//freopen("lx.in","r",stdin);
n=getint();
for(int i=1;i<=n;i++)a[i]=getint()+a[i-1];
a[0]=a[n+1]=-INF;
S=sqrt(n),cnt=(n%S?n/S+1:n/S);
for(int i=1;i<=n;i++)belong[i]=(i-1)/S+1;
for(int i=1;i<=cnt;i++)st[i]=(i-1)*S+1,ed[i]=min(n,i*S);
for(int i=1;i<=cnt;i++)build(i);
int op,x,y,l,r;
ll k,tmp,ans;
m=getint();
while(m--)
{
op=getint(),x=getint(),y=getint();
l=belong[x],r=belong[y];
if(!op)
{
k=getint();
tmp=k*(st[l+1]-x+1);
for(int i=l+1;i<r;i++)
{
fir[i]+=tmp,d[i]+=k;
tmp+=S*k;
}
pushdown(l);
tmp=k;
for(int i=x;i<=min(y,ed[l]);i++)
a[i]+=tmp,tmp+=k;
build(l);
if(l!=r)
{
pushdown(r);
tmp=k*(st[r]-x+1);
for(int i=st[r];i<=y;i++)a[i]+=tmp,tmp+=k;
}
tmp=k*(y-x+1);
for(int i=y+1;i<=ed[r];i++)a[i]+=tmp;
build(r);
for(int i=r+1;i<=cnt;i++)add[i]+=tmp;
}
else
{
ans=-INF;
for(int i=l+1;i<r;i++)ans=max(ans,query(i));
for(int i=x;i<=min(y,ed[l]);i++)ans=max(ans,calc(i));
if(l!=r)for(int i=st[r];i<=y;i++)ans=max(ans,calc(i));
printf("%lld\n",ans);
}
}
return 0;
}