BZOJ4518 [Sdoi2016]征途

[传送门]

 

设最后 $m$ 段每段数字之和为 $a_i$,那么最后答案为 $m^2 \sum \dfrac {\left( a_{i}-\dfrac {s}{m}\right) ^{2}}{m}$
把括号拆开后最后答案为 $m\sum a_i ^{2}-S^{2}$
要求最小化$\sum a_i ^{2}$,那么直接上斜率优化
$dp[i][k]$ 表示在 $i$ 处做第 $k$ 次休息。
那么 $dp[i][k] = min(dp[j][k - 1]) + (sum[i] - sum[j])^2$
$dp[i][k] + 2 \times sum[i] \times sum[j] = dp[j][k - 1] + sum[j] ^ 2 + sum[i] ^ 2$
斜率为 $2 \times sum[i]$,自变量为 $sum[j]$,截距为 $dp[i][k]$,因变量为 $dp[j][k - 1] + sum[j] ^ 2 + sum[i] ^ 2$。
求截距最小值即可。由于斜率单调,由决策单调性可 $O(1)$ 得到最优决策点。
时间复杂度 $O(nm)$
边界为 $dp[i][0] = sum[i] ^ 2$

#include 
#define ll long long
using namespace std;

namespace IO {
    const int MAXSIZE = 1 << 20;
    char buf[MAXSIZE], *p1, *p2;
    #define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, stdin), p1 == p2) ? EOF : *p1++)
    template
    inline void read(T &x) {
        x = 0; T f = 1; char ch = gc();
        while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = gc(); }
        while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = gc(); }
        x *= f;    
    }
} using namespace IO;

const int N = 3e3 + 7;
const double eps = 1e-10;

ll dp[N][2], s[N];
int n, m, que[N], l, r;

inline ll sqr(int x) { return x * x; }
inline ll X(int i) { return s[i]; }
inline ll Y(int i, int cur) { return dp[i][cur] + sqr(s[i]); }
inline ll K(int i, int j, int cur) {
    //if (X(i) == X(j)) return 0;
    return 1.0 * (Y(i, cur) - Y(j, cur)) / (X(i) - X(j));
}
inline int dcmp(double x) {
    if (fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}

int main() {
//    freopen("in.txt", "r", stdin);
    read(n), read(m);
    for (int i = 1; i <= n; i++)
        read(s[i]), s[i] += s[i - 1], dp[i][0] = sqr(s[i]);
    for (int k = 1; k <= m - 1; k++) {
        int cur = k & 1;
        que[l = r = 1] = k;
        for (int i = k + 1; i <= n; i++) {
            while (l < r && dcmp(K(que[l], que[l + 1], cur ^ 1) - 2 * s[i]) < 0) l++;
            int j = que[l];
            dp[i][cur] = dp[j][cur ^ 1] + sqr(s[i] - s[j]);
            while (l < r && dcmp(K(que[r], que[r - 1], cur ^ 1) - K(que[r], i, cur ^ 1)) > 0) r--;
            que[++r] = i;
        }
    }
    printf("%lld\n", m * dp[n][(m - 1) & 1] - sqr(s[n]));
    return 0;
}
View Code

 

你可能感兴趣的:(BZOJ4518 [Sdoi2016]征途)