洛谷 P4983 忘情

我们给定一段长度为\(n\)的序列,现在要求将它分成\(m\)段,要求每一段的\(\frac{\left((\sum\limits_{i=1}^{n}x_i×\bar x)+\bar x\right)^2}{\bar x^2}\)值的总和最小,求出这个最小值。

首先对原式进行化简可得

\[\frac{\left((\sum\limits_{i=1}^{n}x_i×\bar x)+\bar x\right)^2}{\bar x^2}=(\sum_{i=1}^nx_i+1)^2 \]

然后设\(g(i)\)表示序列划分成\(i\)段的最小值,因为\(a^2+b^2>(a+b)^2\),所以\(g\)函数是凸的,于是可以使用wqs二分。

定义\(F(i)\)表示在分一段会多出\(K\)的代价下,分成\(i\)段的最小值。

我们二分斜率\(K\),然后我们要找到一个最小的\(F(i)\)

然后考虑把\(F(i)\)转化为好求的东西,设\(f_i\)表示前\(i\)个数分成若干段的最小值,在转移的时候额外记录一个\(pre_i\)表示前i个数得到最小值的时候分成了多少段,于是可以写出状态转移方程:

\[f_i=Min_{j=0}^{i-1}f_j+(\sum_{k=j+1}^ix_k+1)^2+K \]

\(s_i=\sum_{j=1}^ix_j\),则有

\[f_i=Min_{j=0}^{i-1}f_j+(s_i-s_j)^2+K \]

\[f_i=Min_{j=0}^{i-1}f_j+s_i^2+s_j^2-2s_is_j+2s_i-2s_j+1+K \]

当前需要更新的点为\(i\),假设存在一个决策点\(k<j\)满足\(k\)\(j\)优,那么

\[f_k+s_i^2+s_k^2-2s_is_k+2s_i-2s_k+1+K<f_j+s_i^2+s_j^2-2s_is_j+2s_i-2s_j+1+K \]

\[f_k+s_k^2-2s_k-(f_j+s_j^2-2s_j)<2s_i(s_k-s_j) \]

\(s_k-s_j<0\),所以

\[\frac{f_k+s_k^2-2s_k-(f_j+s_j^2-2s_j)}{s_k-s_j}>2s_i \]

至此我们就可以对上述的dp式子进行斜率优化了。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 1e5;
using namespace std;
int n,m,a[N + 5],s[N + 5],pre[N + 5],q[N + 5];
long long ans,f[N + 5];
long long X(int x)
{
    return f[x] + 1ll * s[x] * s[x] - 2 * s[x];
}
double slope(int x,int y)
{
    return 1.0 * (X(x) - X(y)) / (s[x] - s[y]);
}
int check(long long k)
{
    int l = 1,r = 0;
    q[++r] = 0;
    for (int i = 1;i <= n;i++)
    {
        while (l < r && slope(q[l],q[l + 1]) <= 2 * s[i])
            l++;
        f[i] = X(q[l]) + 1ll * s[i] * s[i] - 2ll * s[i] * s[q[l]] + 2 * s[i] + 1 + k;
        pre[i] = pre[q[l]] + 1;
        while (l < r && slope(q[r],q[r - 1]) >= slope(q[r],i))
            r--;
        q[++r] = i;
    }
    return pre[n];
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i = 1;i <= n;i++)
        scanf("%d",&a[i]),s[i] = s[i - 1] + a[i];
    long long l = 0,r = 1e15,mid;
    while (l <= r)
    {
        mid = l + r >> 1;
        if (check(mid) >= m)
            l = mid + 1,ans = f[n] - 1ll * m * mid;
        else
            r = mid - 1;
    }
    cout<<ans<<endl;
    return 0;
}
posted @ 2020-07-17 16:35  eee_hoho  阅读(88)  评论(0编辑  收藏  举报