bzoj2388: 旅行规划(分块+凸包)

传送门
分块好题。
题意:维护区间加,维护区间前缀和的最大值(前缀和指从1开始的)。


思路:
考虑分块维护答案。
我们把每个点看成 ( i , s u m i ) (i,sum_i) (i,sumi)答案一定会在凸包上,于是我们每个块维护一个凸包。
然后发现 每次前缀和可以分区域修改,在区域内的相当于给所有点的连线加一个斜率,对于区域外的相当于打一个 a d d add add标记,于是给每个块维护整体加标记,斜率增加的首项,斜率的增量标记即可。
查询的时候每个块在凸包上二分一波。
代码:

#include
#define ri register int
using namespace std;
inline int read(){
	int ans=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans*w;
}
typedef long long ll;
const ll inf=1e18;
const int N=1e5+5;
int stk[N],top=0,p[505][505],n,m,sig,st[N],ed[N],len[N],blo[N];
ll sum[N],add[N],fi[N],det[N],a[N];
inline ll calc(int x){return !x||x==n+1?-inf:a[x]+add[blo[x]]+fi[blo[x]]+det[blo[x]]*(x-st[blo[x]]);}
inline double slope(int x,int y){return (double)(a[x]-a[y])/(x-y);}
inline void build(int id){
	stk[top=1]=st[id];
	for(ri i=st[id]+1;i<=ed[id];++i){
		while(top>=2&&slope(stk[top],stk[top-1])<slope(stk[top-1],i))--top;
		stk[++top]=i;
	}
	stk[0]=0,stk[top+1]=n+1,len[id]=top;
	for(ri i=0;i<=top+1;++i)p[id][i]=stk[i];
}
inline void pushdown(int id){
	ll tmp=fi[id];
	for(ri i=st[id];i<=ed[id];++i)a[i]+=tmp,tmp+=det[id],a[i]+=add[id];
	fi[id]=det[id]=add[id]=0;
}
inline void update(int l,int r,ll v){
	int L=blo[l],R=blo[r];
	ll tmp=v*(st[L+1]-l+1);
	for(ri i=L+1;i<R;++i)fi[i]+=tmp,det[i]+=v,tmp+=sig*v;
	pushdown(L);
	tmp=v;
	for(ri i=l;i<=min(r,ed[L]);++i)a[i]+=tmp,tmp+=v;
	build(L);
	pushdown(R);
	if(L^R){
		tmp=v*(st[R]-l+1);
		for(ri i=st[R];i<=r;++i)a[i]+=tmp,tmp+=v;
	}
	tmp=v*(r-l+1);
	for(ri i=r+1;i<=ed[R];++i)a[i]+=tmp;
	build(R);
	for(ri i=R+1;i<=blo[n];++i)add[i]+=tmp;
}
inline ll ask(int id){
	int l=1,r=len[id];
	ll t1,t2,t3;
	while(l<=r){
		int mid=l+r>>1;
		t1=calc(p[id][mid-1]),t2=calc(p[id][mid]),t3=calc(p[id][mid+1]);
		if(t1<t2&&t2<t3)l=mid+1;
		else if(t1>t2&&t2>t3)r=mid-1;
		else return t2;
	}
	return -inf;
}
inline ll query(int l,int r){
	int L=blo[l],R=blo[r];
	ll ret=-inf;
	for(ri i=L+1;i<R;++i)ret=max(ret,ask(i));
	for(ri i=l;i<=min(r,ed[L]);++i)ret=max(ret,calc(i));
	if(L^R)for(ri i=st[R];i<=r;++i)ret=max(ret,calc(i));
	return ret;
}
int main(){
	n=read(),sig=sqrt(n);
	for(ri i=1;i<=n;++i)a[i]=read()+a[i-1],blo[i]=(i-1)/sig+1,ed[blo[i]]=i;
	a[0]=a[n+1]=-inf;
	for(ri i=1;i<=blo[n];++i)st[i]=(i-1)*sig+1,build(i);
	for(ri tt=read(),op,l,r;tt;--tt){
		ll v;
		op=read(),l=read(),r=read();
		if(!op)v=read(),update(l,r,v);
		else cout<<query(l,r)<<'\n';
	}
	return 0;
}

你可能感兴趣的:(#,分块,#,凸包)