【BZOJ 4709】柠檬 斜率优化dp+单调栈

题意

给$n$个贝壳,可以将贝壳分成若干段,每段选取一个贝壳$s_i$,这一段$s_i$的数目为$num$,可以得到$num^2\times s_i$个柠檬,求最多能得到几个柠檬


可以发现只有在一段中首尾颜色相同的情况下最优,所以每次选取一段里末位的$s_i$变成柠檬,于是有$f_i=max_{j \le i}{f_{j-1}+s_i\times(pre_i-pre_j+1)^2}$ ,$pre_i$表示前$i$个贝壳里$s_i$出现了几次

令$j<k$,假设$f_{j-1}+s_i\times(pre_i-pre_j+1)2<f_{k-1}+s_i\times(pre_i-pre_k+1)2$,整理得到$\frac{(f_{j-1}+s_i\times (pre_j-1)^2)-(f_{k-1}+s_i\times (pre_k-1)^2)}{s_i\times (pre_j-pre_k)}<2pre_i$

左边式子为斜率,可以发现满足单调性,利用单调栈优化

时间复杂度$O(n)$

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, s[100005], cnt[10005], pre[100005];
LL dp[100005], col;
vector<int> sta[100005];
inline double slope(int x, int y) {
    return (double)((dp[x - 1] + col * (pre[x] - 1) * (pre[x] - 1)) - (dp[y - 1] + col * (pre[y] - 1) * (pre[y] - 1))) / (double)(col * (pre[x] - pre[y]));
}
int main() {
    scanf("%d", &n);
    int l = 1, r = 0;
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &s[i]); pre[i] = ++cnt[s[i]];
    }
    for(int i = 1; i <= n; ++i) {
        col = s[i]; int top = sta[col].size() - 1;
        while(top > 0 && slope(sta[col][top - 1], sta[col][top]) < slope(sta[col][top], i)) sta[col].pop_back(), --top;
        sta[col].push_back(i); ++top;
        while(top > 0 && slope(sta[col][top - 1], sta[col][top]) < 2 * pre[i]) sta[col].pop_back(), --top;
        dp[i] = dp[sta[col][top] - 1] + col * (pre[i] - pre[sta[col][top]] + 1) * (pre[i] - pre[sta[col][top]] + 1);
    }
    printf("%lld\n", dp[n]);
    return 0;
}
posted @ 2018-03-03 16:43  Ogiso_Setsuna  阅读(173)  评论(0编辑  收藏  举报