#分块,凸壳,二分#洛谷 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;
}