【BZOJ2388】—旅行规划(分块+凸包)

传送门

大意:给定一个序列,要求支持2种操作

1、区间加

2、询问区间ll~rr内最大的一个前缀和


很显然如果我们把每个点的前缀和sumisum_i(i,sumi)(i,sum_i)列在二维平面上

询问就变成了求ll~rr内的上凸包

由于线段树之类的无法维护

于是考虑分块维护上凸包

每次整块修改不会改变内部凸包

散块修改后暴力重构

由于我们记录的是一个前缀和

则区间加就变成了加一个等差数列

rr之后则是加一个值

所以维护一下firfir数列开头,deldel公差,addadd

维护一下

复杂度O(nnlogn)O(n\sqrt n log_n)

常数较小跑的比较快

#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';
		}
	}
}
posted @ 2019-01-22 15:33  Stargazer_cykoi  阅读(139)  评论(0编辑  收藏  举报