#4212. 旅行规划(travel)

题意

OIVillage 是一个风景秀美的乡村,为了更好的利用当地的旅游资源,吸引游客,推动经济发展,xkszltl 决定修建了一条铁路将当地 n n n 个最著名的经典连接起来,让游客可以通过火车从铁路起点( 1 1 1 号景点)出发,依次游览每个景区。为了更好的评价这条铁路,xkszltl 为每一个景区都赋予了一个美观度,而一条旅行路径的价值就是它所经过的景区的美观度之和。不过,随着天气与季节的变化,某些景点的美观度也会发生变化。

xkszltl 希望为每位旅客提供最佳的旅行指导,但是由于游客的时间有限,不一定能游览全部景区,然而他们也不希望旅途过于短暂,所以每个游客都希望能在某一个区间内的车站结束旅程,而 xkszltl 的任务就是为他们选择一个终点使得旅行线路的价值最大。可是当地的景点与前来观光的旅客实在是太多了,xkszltl 无法及时完成任务,于是找到了准备虐杀 NOI2019 的你,希望你能帮助他完成这个艰巨的任务。

题解

考虑分块凸包
s i s_i si 表示i的前缀和, d j d_j dj 表示 j j j 这个区间的每个数都加上它, f j f_j fj 表示每位置的前缀和都加上它
对于第 j j j 块的区间 [ l , r ] [l,r] [l,r] 考虑它的 a n s ans ans
a n s = m a x i = l r f j + d j × ( i − l ) + s i ans=max_{i=l}^{r} f_j+d_j \times (i-l)+s_i ans=maxi=lrfj+dj×(il)+si
x = i − l x=i-l x=il y = s i y=s_i y=si ,把式子化简成 y = − d j × x + a n s − f j y=-d_j \times x+ans-f_j y=dj×x+ansfj 故维护上凸壳即可

#include <bits/stdc++.h>
#define I inline
#define db double
#define LL long long
using namespace std;
const int N=1e5+5,M=350;
LL s[N],f[M],d[M],c[M];
int m,n,B,b[N],L[M],R[M],Z,S[M],p[M][M],sz[M];
I db K(int x,int y){return (db)(s[y]-s[x])/(db)(y-x);}
I void U(int x){
	int t=0;S[++t]=L[x];
	for (int i=L[x]+1;i<=R[x];i++){
		while(t>1 && K(S[t-1],S[t])<K(S[t-1],i))
			t--;
		S[++t]=i;
	}
	sz[x]=t;p[x][t+1]=n+1;
	for (int i=1;i<=t;i++) p[x][i]=S[i];
}
I void D(int x){
	LL t=f[x];
	for (int i=L[x];i<=R[x];i++)
		s[i]+=t+c[x],t+=d[x];
	f[x]=c[x]=d[x]=0;
}
I void C(int l,int r,LL k){
	int x=b[l],y=b[r];LL t=k*(L[x+1]-l+1);
	for (int i=x+1;i<y;i++)
		f[i]+=t,d[i]+=k,t+=k*(R[i]-L[i]+1);
	D(x);t=k;
	for (int i=l;i<=R[x] && i<=r;i++)
		s[i]+=t,t+=k;
	U(x);D(y);if (x!=y){
		t=k*(L[y]-l+1);
		for (int i=L[y];i<=r;i++)
			s[i]+=t,t+=k;
	}t=k*(r-l+1);
	for (int i=r+1;i<=R[y];i++) s[i]+=t;
	U(y);for (int i=y+1;i<=Z;i++) c[i]+=t;
}
I LL G(int x){
	if (!x || x>n) return (LL)-2e18;
	int y=b[x];return s[x]+c[y]+f[y]+d[y]*(x-L[y]);
}
I LL A(int x){
	int l=1,r=sz[x];
	while(l<=r){
		int mid=(l+r)>>1;
		LL t1=G(p[x][mid-1]);
		LL t2=G(p[x][mid]);
		LL t3=G(p[x][mid+1]);
		if (t1<t2 && t2<t3) l=mid+1;
		else if (t1>t2 && t2>t3) r=mid-1;
		else return t2;
	}
}
I LL Q(int l,int r){
	int x=b[l],y=b[r];LL t=-2e18;
	for (int i=x+1;i<y;i++)
		t=max(A(i),t);
	for (int i=l;i<=R[x] && i<=r;i++)
		t=max(G(i),t);if (x!=y)
	for (int i=L[y];i<=r;i++)
		t=max(G(i),t);return t;
}
int main(){
	scanf("%d",&n);B=sqrt(n);
	for (int x,i=1;i<=n;i++){
		scanf("%d",&x);
		s[i]=s[i-1]+x;
		b[i]=(i-1)/B+1;
		if (b[i]!=b[i-1])
			L[b[i]]=i,R[b[i-1]]=i-1;
	}R[Z=b[n]]=n;
	for (int i=1;i<=Z;i++) U(i);
	scanf("%d",&m);LL x;
	for (int op,l,r;m--;){
		scanf("%d%d%d",&op,&l,&r);
		if (!op) scanf("%lld",&x),C(l,r,x);
		else printf("%lld\n",Q(l,r));
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值