【BZOJ2388】—旅行规划(分块+凸包)
大意:给定一个序列,要求支持2种操作
1、区间加
2、询问区间~内最大的一个前缀和
很显然如果我们把每个点的前缀和以列在二维平面上
询问就变成了求~内的上凸包
由于线段树之类的无法维护
于是考虑分块维护上凸包
每次整块修改不会改变内部凸包
散块修改后暴力重构
由于我们记录的是一个前缀和
则区间加就变成了加一个等差数列
对之后则是加一个值
所以维护一下数列开头,公差,加
维护一下
复杂度
常数较小跑的比较快
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char ch=getchar();
int res=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return res*f;
}
#define ll long long
const int N=100005;
const int M=350;
const ll inf=100000000000000;
int n,m,len,cnt,plc[N],l[M],r[M],num[M],stk[N],con[M][M];
ll sum[N],fir[M],del[M],add[M];
inline double slop(int x,int y){
return (double)(sum[y]-sum[x])/(y-x);
}
inline ll calc(int x){
if(x==0||x==n+1)return -inf;
return sum[x]+fir[plc[x]]+del[plc[x]]*(x-l[plc[x]])+add[plc[x]];
}
inline void reset(int x){
int top=0;stk[++top]=l[x];
for(int i=l[x]+1;i<=r[x];i++){
while(top>=2&&slop(stk[top-1],stk[top])<slop(stk[top-1],i))
top--;
stk[++top]=i;
}
num[x]=top;
stk[++top]=n+1;
for(int i=0;i<=top;i++)con[x][i]=stk[i];
}
inline void pushdown(int x){
ll tmp=fir[x];
for(int i=l[x];i<=r[x];i++){
sum[i]+=tmp,tmp+=del[x],sum[i]+=add[x];
}
del[x]=add[x]=fir[x]=0;
}
inline void update(int x,int y,ll k){
pushdown(plc[x]);
ll tmp=k;
for(int i=x;i<=min(y,r[plc[x]]);i++){
sum[i]+=tmp,tmp+=k;
}
reset(plc[x]);
for(int i=plc[x]+1;i<=plc[y]-1;i++){
fir[i]+=tmp,del[i]+=k,tmp+=len*k;
}
pushdown(plc[y]);
if(plc[x]!=plc[y]){
for(int i=l[plc[y]];i<=y;i++){
sum[i]+=tmp,tmp+=k;
}
}
tmp-=k;//因为为了后面的计算所以每次加一个k,但在最后一个点的时候后面是不加的,所以要减去一个k
for(int i=y+1;i<=r[plc[y]];i++)sum[i]+=tmp;
reset(plc[y]);
for(int i=plc[y]+1;i<=cnt;i++)add[i]+=tmp;
}
inline ll find(int x){
int l=1,r=num[x];
ll a1,a2,a3;
while(l<=r){
int mid=(l+r)>>1;
a1=calc(con[x][mid-1]),a2=calc(con[x][mid]),a3=calc(con[x][mid+1]);
if(a1<a2&&a2<a3)l=mid+1;
else if(a1>a2&&a2>a3)r=mid-1;
else return a2;
}
}
inline ll query(int x,int y){
ll ans=-inf;
for(int i=plc[x]+1;i<=plc[y]-1;i++){
ans=max(ans,find(i));
}
for(int i=x;i<=min(y,r[plc[x]]);i++){
ans=max(ans,calc(i));
}
if(plc[x]!=plc[y]){
for(int i=l[plc[y]];i<=y;i++)
ans=max(ans,calc(i));
}
return ans;
}
int main(){
n=read();
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+read();
}
sum[0]=sum[n+1]=-inf;
len=sqrt(n),cnt=(n-1)/len+1;
for(int i=1;i<=n;i++)plc[i]=(i-1)/len+1;
for(int i=1;i<=cnt;i++)l[i]=(i-1)*len+1,r[i]=i*len;
for(int i=1;i<=cnt;i++)reset(i);
m=read();
for(int i=1;i<=m;i++){
int op=read();
if(op==0){
int x=read(),y=read(),k=read();
update(x,y,k);
}
else {
int x=read(),y=read();
cout<<query(x,y)<<'\n';
}
}
}