Luogu

分析

感觉对斜率优化那一套更熟练了呢 qwq

首先有一个显然的性质:每一段左右端的贝壳大小一定相同,而且这一段的 $s_0$ 一定是左右端贝壳的大小。因为如果不是这样的话,我们显然可以把端点单独成一段,然后答案会变大。

设 $dp_i$ 表示前 $i$ 个贝壳最多可以得到多少柠檬,$c_i$ 表示 $i$ 是第几个 $a_i$ 大小的柠檬,容易得到状态转移方程

$$ dp_i=\max_{j=1}^i\left\{dp_{j-1}+a_i(c_i-c_j+1)^2\right\} $$

把 $\max$ 里的东西拆开,并把所有项分为三类:

  • 与 $j$ 无关的:$a_ic_i\!^2+2a_ic_i+a_i$
  • 只和 $j$ 有关的:$dp_{j-1}+a_ic_j\!^2-2a_ic_j$(注意, $a_i=a_j$)
  • 既和 $i$ 有关又和 $j$ 有关的:$-2a_ic_ic_j$

令 $pre_i=a_ic_i\!^2+2a_ic_i+a_i$,$k_i=2c_i$,$x_i=a_ic_i$,$y_i=dp_{i-1}+a_ic_i\!^2-2a_ic_i$,那么状态转移方程改写为:

$$ dp_i=pre_i+\max_{j=1}^{i}\left\{-k_ix_j+y_j\right\} $$

因为要求的是最大值,所以维护上凸壳;因为 $x_i$ 单增、$k_i$ 单增,所以用单调栈维护上凸壳,栈顶为最优决策。

注意每次要把自己先加到单调栈中去再取栈顶转移。

代码

// ===================================
//   author: M_sea
//   website: http://m-sea-blog.com/
// ===================================
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#include <stack>
#define re register
#define sqr(x) ((x)*(x))
#define int long long
using namespace std;

inline int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=100000+10,V=10000+10;

int n,a[N],o[V],c[N];
int dp[N];
vector<int> Q[V];

inline int X(int i) { return a[i]*c[i]; }
inline int Y(int i) { return dp[i-1]+a[i]*c[i]*c[i]-2*a[i]*c[i]; }
inline double slope(int i,int j) {
    return 1.0*(Y(i)-Y(j))/(X(i)-X(j));
}
inline int calc(int i,int j) {
    return dp[j-1]+a[i]*sqr(c[i]-c[j]+1);
}

#define A Q[t][Q[t].size()-2]
#define B Q[t][Q[t].size()-1]

signed main() {
    n=read();
    for (re int i=1;i<=n;++i)
        a[i]=read(),c[i]=c[o[a[i]]]+1,o[a[i]]=i;
    for (re int i=1;i<=n;++i) { int t=a[i];
        while (Q[t].size()>=2&&slope(A,i)>=slope(A,B)) Q[t].pop_back();
        Q[t].push_back(i);
        while (Q[t].size()>=2&&calc(i,B)<=calc(i,A)) Q[t].pop_back();
        dp[i]=calc(i,Q[t].back());
    }
    printf("%lld\n",dp[n]);
    return 0;
}
最后修改:2019 年 10 月 23 日 10 : 39 AM