[APIO2014]序列分割

题意

Here

思考

这题竟然卡常 \(+\) 卡精度,我 \(……\)

\(f[i][k]\) 表示前 \(i\) 个元素分了 \(k\) 次的最大值,我们可以得出转移方程(\(sum[]\)为前缀和数组):

\[f[i][k] = max\{f[j][k-1] + sum[j] * (sum[i] - sum[j])\} \]

这一个转移朴素来算是 \(O(n^2k)\) 的复杂度,我们不妨把式子拆开:

\[f[i][k] = f[j][k-1] + sum[j] * (sum[i] - sum[j])\\ = f[j][k-1] + sum[i]*sum[j] - sum[j] ^ 2\\ f[i][k] - sum[i] * sum[j] = f[j][k-1] - sum[j]^2\]

\(f[i][k]\) 看作 \(y\) 上截距, \(sum[j]\) 看作自变量, \(f[j][k-1] - sum[j]^2\) 看作因变量,\(-sum[i]\) 则为斜率,由于\(-sum[i]\) 单调递减,我们可以考虑斜率优化,维护一个上凸包,复杂度为 \(O(nk)\),空间的话,\(f[][]\) 数组可以滚动,但是我太懒了,所以没滚\(……\)

由于卡常,我开了 \(O2\) (我太菜了)

有一点要注意的就是计算斜率的时候分母有可能为 \(0\),我们特判一下

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double D;
const int N = 100010;
const int K = 220;
ll f[N][K], a[N], sum[N], q[N], road[N][K], n, kmax;
D X(int j){ return (D)sum[j]; }
D Y(int j, int k){ return (D)f[j][k-1] - (D)sum[j] * (D)sum[j]; }
D slope(int i, int j, int k){
    if(sum[i] == sum[j]) return -0x3f3f3f3f;
    return ( Y(i, k) - Y(j, k) ) / ( X(i) - X(j) );
}
void print(int i, int k){
    if(k == 0) return;
    print(road[i][k], k-1);
    printf("%lld ", road[i][k]);
}
int main(){
    scanf("%lld%lld", &n, &kmax);
    for(int i=1; i<=n; i++) scanf("%lld", &a[i]), sum[i] = sum[i-1] + a[i];
    for(int k=1; k<=kmax; k++){
        int h = 1, t = 1; q[1] = 0;
        for(int i=1; i<=n; i++){
            while(h < t && slope(q[h], q[h + 1], k) >= -sum[i] ) h ++;
            f[i][k] = f[q[h]][k-1] + sum[q[h]] * (sum[i] - sum[q[h]]);
            road[i][k] = q[h];
            while(h < t && slope(q[t - 1], q[t], k) <= slope(q[t], i, k) ) t --;
            q[++t] = i;
        }
    }
    printf("%lld\n", f[n][kmax]);
    print(n, kmax);
    return 0;
}

总结

注意精度??

注意分母为 \(0\) 的特判

注意常数优化

posted @ 2018-12-06 22:46  alecli  阅读(110)  评论(0编辑  收藏  举报