P4192 旅行规划 题解
要求支持操作:区间加,区间询问前缀和的最大值。\(n\le 10^5,|a_i| \le 10^9\)。
由于存在负数,我们的前缀和并不具有单调性。
考虑把操作对前缀和数组的影响写出来,对于 \(l \le i \le r\) 的位置,前缀和 \(s_i\) 加上了 \(k\times (i-l+1)\),对于 \(>r\) 的位置,前缀和加上了 \(k\times (r-l+1)\),后面这个显然是平凡的区间加法,考虑前面一项怎么做。
这种等差数列的加法有一个特点,即等差数列加上另一个等差数列还是等差数列,那我们可以考虑用懒标记存储当前的公差,自然想到分块,每一块维护 \(tag_p,tk_p\) 分别表示此块的加法标记和公差标记。
为了方便的得到每一位置的值,我们令 \(ans_i = s_i+tag_p+tk_p\times i\),则对于修改操作,我们给 \(tk_p\) 加上 \(k\),也要给 \(tag_p\) 加上 \(k\times (1-l)\)。
那我们怎么获得区间内的最大前缀和呢?观察 \(ans_i\) 的形式,发现可以斜率优化,每个块维护 \((i,s_i)\) 的上凸壳,二分斜率 \(-tk_p\) 来获取最大截距。
那基本就做完了,注意散块修改要暴力重构整个块。
时间复杂度 \(O(n\sqrt n\log n)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m,t;
const int N = 1e5+10,B=355,inf=1e18;
int a[N],s[N];
struct node{
int x,y;
};
struct block{
node q[B];
int hd=1,tl=0;
void clr(){
hd=1,tl=0;
}
void add(node c){
while(hd<=tl-1){
node a=q[tl-1],b=q[tl];
if((c.y-b.y)*(b.x-a.x)>=(b.y-a.y)*(c.x-b.x))tl--;
else break;
}
q[++tl]=c;
}
bool check(int i,int j,int k){
return (q[j].y-q[i].y)>=k*(q[j].x-q[i].x);
}
int find(int k){
int l=hd+1,r=tl,res=hd;
while(l<=r){
int mid=(l+r)/2;
if(check(mid-1,mid,k)){
res=mid;
l=mid+1;
}else{
r=mid-1;
}
}
return res;
}
}b[B];
int L[B],R[B],pos[N],tk[B],tag[B];
void pushdown(int p){
if(!tag[p]&&!tk[p])return;
for(int i=L[p];i<=R[p];i++){
s[i]+=tag[p]+i*tk[p];
}
tag[p]=tk[p]=0;
}
void rebuild(int p){
b[p].clr();
for(int i=L[p];i<=R[p];i++){
b[p].add({i,s[i]});
}
}
void modify(int l,int r,int k){
int p=pos[l],q=pos[r];
if(p==q){
pushdown(p);
for(int i=l;i<=r;i++)s[i]+=(i-l+1)*k;
rebuild(p);
}else{
pushdown(p);
for(int i=l;i<=R[p];i++)s[i]+=(i-l+1)*k;
rebuild(p);
for(int i=p+1;i<q;i++){
tk[i]+=k;tag[i]+=k*(1-l);
}
pushdown(q);
for(int i=L[q];i<=r;i++)s[i]+=(i-l+1)*k;
rebuild(q);
}
}
void update(int l,int r,int v){
int p=pos[l],q=pos[r];
if(p==q){
pushdown(p);
for(int i=l;i<=r;i++)s[i]+=v;
rebuild(p);
}else{
pushdown(p);
for(int i=l;i<=R[p];i++)s[i]+=v;
rebuild(p);
for(int i=p+1;i<q;i++){
tag[i]+=v;
}
pushdown(q);
for(int i=L[q];i<=r;i++)s[i]+=v;
rebuild(q);
}
}
int query(int l,int r){
int p=pos[l],q=pos[r],res=-inf;
if(p==q){
for(int i=l;i<=r;i++){
res=max(res,s[i]+i*tk[p]+tag[p]);
}
}else{
for(int i=l;i<=R[p];i++){
res=max(res,s[i]+i*tk[p]+tag[p]);
}
for(int i=p+1;i<q;i++){
int x=b[i].find(-tk[i]);
res=max(res,b[i].q[x].y+tk[i]*b[i].q[x].x+tag[i]);
}
for(int i=L[q];i<=r;i++){
res=max(res,s[i]+i*tk[q]+tag[q]);
}
}
return res;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
t=sqrt(n);
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=s[i-1]+a[i];
}
for(int i=1;i<=t;i++){
L[i]=(i-1)*t+1;
R[i]=i*t;
}
if(R[t]<n)t++,L[t]=R[t-1]+1,R[t]=n;
for(int i=1;i<=t;i++){
for(int j=L[i];j<=R[i];j++){
pos[j]=i;
b[i].add({j,s[j]});
}
}
cin>>m;
while(m--){
int op,l,r,k;
cin>>op>>l>>r;
if(op==0){
cin>>k;
modify(l,r,k);
if(r<n)update(r+1,n,(r-l+1)*k);
}else{
cout<<query(l,r)<<endl;
}
}
return 0;
}

浙公网安备 33010602011771号