题解:P8067 [BalkanOI 2012] balls

题意

给出一个长为 \(n\) 的序列,让你选择一段长度 \(\ge 2\) 区间内所有值变为区间右或左端点的值,最大化操作后的权值和。

思路

以第一问为例,选择一段区间 \((l,r]\) 后权值和的变化量为:

\[(r-l)\times a_r-(sum_r-sum_l) \]

其中 \(sum_i=\sum\limits_{j=1}^{i}a_j\)。把式子拆开,发现是如下的形式:

\[r\times a_r-sum_r-l\times a_r+sum_l \]

\(r\) 固定时,这是一个关于 \(l\) 的一次函数。不妨设 \(y=-a_r\times l+sum_l\),那么要最大化原式的值,就是要最大化这条直线的斜率。于是套路地在凸包上三分即可。

第二问把第一问的数组翻转再跑一遍即可。

代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
constexpr int N=3e5+5;
int n,a[N];
ll sum[N];
pair<int,ll>operator-(const pair<int,ll>&x,const pair<int,ll>&y){ // 计算x->y的向量
    return {x.first-y.first,x.second-y.second};
}
ll operator*(const pair<int,ll>&x,const pair<int,ll>&y){ // 计算向量叉积
    return x.first*y.second-x.second*y.first;
}
struct ConvexHull{ // 凸包
    vector<pair<int,ll>>st;
    void clear(){st.clear();}
    void insert(pair<int,ll>x){
        while(st.size()>=2&&(st[st.size()-2]-st.back())*(x-st.back())<=0)st.pop_back();
        st.push_back(x);
    }
    ll query(ll k){
        assert(st.size());
        if(st.size()==1)return k*st[0].first+st[0].second;
        int l=0,r=st.size()-1;
        while(r-l>1){ // 三分
            const int mid=(l+r)>>1;
            if(st[mid-1].first*k+st[mid-1].second<st[mid+1].first*k+st[mid+1].second)l=mid;
            else r=mid;
        }
        return max(k*st[l].first+st[l].second,k*st[r].first+st[r].second);
    }
}ch;
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
    ch.clear();ch.insert({0,0});
    ll ans=-1e18;
    for(int i=2;i<=n;i++){ // 注意区间左右端点不能相同,所以从2开始
        ans=max(ans,ch.query(-a[i])-sum[i]+1ll*i*a[i]);
        ch.insert({i-1,sum[i-1]});
    }
    cout<<sum[n]+ans<<'\n';
    reverse(a+1,a+n+1);
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
    ch.clear();ch.insert({0,0});
    ans=-1e18;
    for(int i=2;i<=n;i++){
        ans=max(ans,ch.query(-a[i])-sum[i]+1ll*i*a[i]);
        ch.insert({i-1,sum[i-1]});
    }
    cout<<sum[n]+ans<<'\n';
    return 0;
}
posted @ 2025-09-24 13:15  Tachanka233  阅读(3)  评论(0)    收藏  举报