B e a r <mtext>   </mtext> a n d <mtext>   </mtext> B o w l i n g <mtext>   </mtext> 4 Bear\ and\ Bowling\ 4 Bear and Bowling 4


D e s c r i p t i o n \mathcal{Description} Description
给一个长度为 N N N 的序列 { a i } \{a_i\} {ai}, 求 <munderover> max x = 1 N k + 1 </munderover> ( <munderover> i = 1 k </munderover> i a x + i 1 ) \max\limits_{x=1}^{N-k+1}( \sum\limits_{i=1}^k i\cdot a_{x+i-1} ) x=1maxNk+1(i=1kiax+i1)

1 n 2 × 1 0 5 , a i 1 0 7 1\leq n\leq 2\times 10^5, |a_i|\leq 10^7 1n2×105,ai107


最初想法
暴力 : O ( N ) O(N) O(N) 枚举 k k k, 再配合前缀和 O ( N ) O(N) O(N) 计算答案, 取最优值, 总共 O ( N 2 ) O(N^2) O(N2).
正解要斜率优化, 回去学习…


正解部分
n n n 年后, 斜率优化看懂一点了… 没学过的可以点击 这里.

s u m 1 [ i ] sum_1[i] sum1[i] 表示前 i i i a i a_i ai 的前缀和, s u m 2 [ i ] sum_2[i] sum2[i] 表示前 i i i i a i i*a_i iai的前缀和, v a l [ i , j ] val[i,j] val[i,j]表示 i , j i,j i,j之间的价值 .
v a l [ i , j ] = s u m 2 [ j ] s u m 2 [ i 1 ] ( i 1 ) ( s u m 1 [ j ] s u m 1 [ i 1 ] ) val[i, j] = sum_2[j] - sum_2[i-1] - (i-1)*(sum_1[j] - sum_1[i-1]) val[i,j]=sum2[j]sum2[i1](i1)(sum1[j]sum1[i1])

化简为 y = k x + b y=kx+b y=kx+b 的形式进行 斜率优化,
( i 1 ) s u m 1 [ i 1 ] s u m 2 [ i 1 ] = ( i 1 ) s u m 1 [ j ] + v a l [ i , j ] s u m 2 [ j ] (i-1)sum_1[i-1]-sum_2[i-1]=(i-1)sum_1[j]+val[i,j]-sum_2[j] (i1)sum1[i1]sum2[i1]=(i1)sum1[j]+val[i,j]sum2[j]
此时

  • y = ( i 1 ) s u m 1 [ i 1 ] s u m 2 [ i 1 ] y=(i-1)sum_1[i-1]-sum_2[i-1] y=(i1)sum1[i1]sum2[i1]
  • k = s u m 1 [ j ] k=sum_1[j] k=sum1[j]
  • x = i 1 x=i-1 x=i1
  • b = v a l [ i , j ] s u m 2 [ j ] b=val[i,j] - sum_2[j] b=val[i,j]sum2[j].

鉴于求的是最大值, 所以维护上凸壳.
k k k的正负无法确定, 于是不能使用弹掉队首的方法去找最优决策点 , 只能使用二分去查找队列中第一个比 小于等于 k k k 的斜率 .

于是枚举右端点 j j j 的同时将 j j j 作为一个新的决策加入斜率优化的单调队列中, 用来对以后的 j j j 提供决策 i i i,


实现部分

#include<cstdio>
#include<algorithm>
#define reg register
typedef long long ll;

const int maxn = 2e5 + 10;

int N;
int A[maxn];

ll Q[maxn];
ll sum1[maxn];
ll sum2[maxn];

double X(int i){ return i; }

double Y(int i){ return i*sum1[i] - sum2[i]; }; //!

double slope(int i, int j){ return ( Y(i)-Y(j) ) / ( X(i)-X(j) ); }

ll Calc(int i, int j){ return sum2[j]-sum2[i]-1ll*i*(sum1[j]-sum1[i]); }

int main(){
        scanf("%d", &N);
        for(reg int i = 1; i <= N; i ++){
                scanf("%d", &A[i]);
                sum1[i] = sum1[i-1] + A[i];
                sum2[i] = sum2[i-1] + 1ll*A[i]*i;
        }
        int tail = 0; ll Ans = 0;
        for(reg int j = 1; j <= N; j ++){
                ll k = sum1[j];
                int l = 0, r = tail;
                while(l < r){
                        int mid = l+r >> 1;
                        if(slope(Q[mid], Q[mid+1]) > k) l = mid+1;
                        else r = mid;
                }
                int i = Q[r];
                Ans = std::max(Ans, Calc(i, j));
                while(tail >= 1 && slope(Q[tail], Q[tail-1]) < slope(Q[tail], j)) tail --;
                Q[++ tail] = j;
        }
        printf("%I64d\n", Ans);
        return 0;
}