CF660F Bear and Bowling 4 [斜率优化动态规划]

Bear and Bowling 4Bear\ and\ Bowling\ 4


Description\mathcal{Description}
给一个长度为NN 的序列 {ai}\{a_i\}, 求 maxx=1Nk+1(i=1kiax+i1)\max\limits_{x=1}^{N-k+1}( \sum\limits_{i=1}^k i\cdot a_{x+i-1} )

1n2×105,ai1071\leq n\leq 2\times 10^5, |a_i|\leq 10^7


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


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

sum1[i]sum_1[i] 表示前 iiaia_i 的前缀和, sum2[i]sum_2[i] 表示前 iiiaii*a_i的前缀和, val[i,j]val[i,j]表示i,ji,j之间的价值 .
val[i,j]=sum2[j]sum2[i1](i1)(sum1[j]sum1[i1])val[i, j] = sum_2[j] - sum_2[i-1] - (i-1)*(sum_1[j] - sum_1[i-1])

化简为 y=kx+by=kx+b 的形式进行 斜率优化,
(i1)sum1[i1]sum2[i1]=(i1)sum1[j]+val[i,j]sum2[j](i-1)sum_1[i-1]-sum_2[i-1]=(i-1)sum_1[j]+val[i,j]-sum_2[j]
此时

  • y=(i1)sum1[i1]sum2[i1]y=(i-1)sum_1[i-1]-sum_2[i-1]
  • k=sum1[j]k=sum_1[j]
  • x=i1x=i-1
  • b=val[i,j]sum2[j]b=val[i,j] - sum_2[j].

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

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


实现部分

#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;
}
posted @ 2019-07-16 00:17  XXX_Zbr  阅读(319)  评论(0编辑  收藏  举报