#分块,凸壳,二分#洛谷 4192 旅行规划

题目传送门


分析

给初始数组做前缀和后,询问就相当于区间最大值,而修改就是

\(x\)\(y\) 加上首项为 \(k\) 公差为 \(k\) 的等差数列,

\(y+1\)\(n\) 加上常数为 \(k*(y-x+1)\) 的常数列。

查询比较困难,考虑分块,散块直接重构,整块相当于是维护块的首项和公差

记为 \(st[B]\)\(delta[B]\),那么对于整块如何查询呢?

如果答案被表示为 \(a[i]+st[B]+i*delta[B]\),其斜率视为 \(-delta[B]\)

点视为 \((i,a[i]+st[B])\),那么相当于是维护斜率单调递减的上凸壳,对于 \(-delta[B]\) 进行二分


代码

#include <cstdio>
#include <cctype>
#include <cmath>
#include <stack>
#include <algorithm>
using namespace std;
const int N=100011,M=1011; typedef long long lll;
int n,bl,pos[N],L[N],R[N],b[M][M],len[M]; lll a[N],delta[M],st[M];
lll iut(){
	lll ans=0,f=1; char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans*f;
}
void print(lll ans){
	if (ans<0) putchar('-'),ans=-ans;
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
double slope(int i,int j){return (double)(a[i]-a[j])/(i-j);}
lll query(int block){
	int l=1,r=len[block];
	while (l<r){
		int mid=(l+r)>>1;
		if (-delta[block]>=slope(b[block][mid+1],b[block][mid])) r=mid;
		    else l=mid+1;
	}
	return a[b[block][l]]+st[block]+b[block][l]*delta[block];
}
void build(int block){
	len[block]=0;
	for (int i=L[block];i<=R[block];++i){
		while (len[block]>1&&slope(i,b[block][len[block]-1])>=slope(b[block][len[block]],b[block][len[block]-1])) --len[block];
		b[block][++len[block]]=i;
	}
}
lll Query(int x,int y){
	lll ans=-1e18;
	if (pos[x]==pos[y]){
		if (x==L[pos[x]]&&y==R[pos[y]]) ans=query(pos[x]);
		    else for (int i=x;i<=y;++i) ans=max(ans,a[i]+st[pos[x]]+i*delta[pos[x]]);
		return ans;
	}
	if (x==L[pos[x]]) ans=max(ans,query(pos[x]));
	else for (int i=x;i<=R[pos[x]];++i)
	    ans=max(ans,a[i]+st[pos[x]]+i*delta[pos[x]]);
	for (int i=pos[x]+1;i<pos[y];++i) ans=max(ans,query(i));
	if (R[pos[y]]==y) ans=max(ans,query(pos[y]));
	else for (int i=L[pos[y]];i<=y;++i)
	    ans=max(ans,a[i]+st[pos[y]]+i*delta[pos[y]]);
	return ans;
}
void Update(int x,int y,lll z){
	if (pos[x]==pos[y]){
		if (x==L[pos[x]]&&y==R[pos[y]]) st[pos[x]]+=(1-x)*z,delta[pos[x]]+=z;
		else{
			for (int i=x;i<=y;++i) a[i]+=z*(i-x+1);
			for (int i=y+1;i<=R[pos[x]];++i) a[i]+=z*(y-x+1);
			for (int i=L[pos[x]];i<=R[pos[x]];++i) a[i]+=st[pos[x]]+i*delta[pos[x]];
			st[pos[x]]=delta[pos[x]]=0;
			build(pos[x]);
		}
	}else{
		if (x==L[pos[x]]) st[pos[x]]+=(1-x)*z,delta[pos[x]]+=z;
		else{
			for (int i=x;i<=R[pos[x]];++i) a[i]+=z*(i-x+1);
			for (int i=L[pos[x]];i<=R[pos[x]];++i) a[i]+=st[pos[x]]+i*delta[pos[x]];
			st[pos[x]]=delta[pos[x]]=0;
			build(pos[x]);
		}
		for (int i=pos[x]+1;i<pos[y];++i) st[i]+=(1-x)*z,delta[i]+=z;
		if (R[pos[y]]==y) st[pos[y]]+=(1-x)*z,delta[pos[y]]+=z;
		else{
			for (int i=L[pos[y]];i<=y;++i) a[i]+=z*(i-x+1);
			for (int i=y+1;i<=R[pos[y]];++i) a[i]+=z*(y-x+1);
			for (int i=L[pos[y]];i<=R[pos[y]];++i) a[i]+=st[pos[y]]+i*delta[pos[y]];
			st[pos[y]]=delta[pos[y]]=0;
			build(pos[y]);
		}
	}
	for (int i=pos[y]+1;i<=pos[n];++i) st[i]+=z*(y-x+1);
}
int main(){
	n=iut(),bl=sqrt(n);
	for (int i=1;i<=n;++i) a[i]=a[i-1]+iut(),pos[i]=(i-1)/bl+1;
	for (int i=1;i<=n;++i){
		if (!L[pos[i]]) L[pos[i]]=i;
		R[pos[i]]=i;
	}
	for (int i=1;i<=pos[n];++i) build(i);
	for (int Q=iut();Q;--Q){
		int opt=iut(),x=iut(),y=iut(); lll z;
		if (opt==1) print(Query(x,y)),putchar(10);
		    else z=iut(),Update(x,y,z);
	}
	return 0;
}
posted @ 2025-07-10 23:26  lemondinosaur  阅读(8)  评论(1)    收藏  举报