P4192 旅行规划 题解

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;
}
posted @ 2026-10-22 19:46  farenara_sayowell  阅读(3)  评论(0)    收藏  举报