[SDOI2016]征途

题目链接

Solution

方差

=\(\frac{\displaystyle\sum^{m}_{i=1}{{(x_i-\bar{x})^2}}}{m}\)

=\(\displaystyle\sum^{m}_{i=1}{({x_i^2} + \bar{x}^2 - 2*x_i*\bar{x})}\)

=\(\displaystyle\sum^{m}_{i=1}{(x_i^2+\frac{\displaystyle\sum^{m}_{i=1}x_i}{m}- 2*x_i*\frac{\displaystyle\sum^{m}_{i=1}x_i}{m})}\)

=\(\displaystyle\sum^{m}_{i=1}{x_i^2}+\displaystyle\sum^{m}_{i=1}x_i-\frac{\displaystyle\sum^{m}_{i=1}(2*x_i*\displaystyle\sum^{m}_{i=1}x_i)}{m}\)

=\(\displaystyle\sum^{m}_{i=1}{x_i^2}+\displaystyle\sum^{m}_{i=1}x_i-\frac{\displaystyle\sum^{m}_{i=1}x_i*\displaystyle\sum^{m}_{i=1}(2*x_i)}{m}\)

=\(\displaystyle\sum^{m}_{i=1}{x_i^2}+\displaystyle\sum^{m}_{i=1}x_i-\frac{2*(\displaystyle\sum^{m}_{i=1}x_i)^2}{m}\)

易得\(\displaystyle\sum^{m}_{i=1}x_i\)\(\displaystyle\sum^{m}_{i=1}x_i\)是定值

所以只需要求\(\displaystyle\sum^{m}_{i=1}{x_i^2}\)的最小值就行了

然后再套上公式 注意得*上\(m^2\)

然后非常容易想到DP

定义

dp[i][j]表示前i个分成j份的平方和的最小值

易得状态转移方程

\(\color{pink}{dp[i][j] = min(dp[k][j-1]+(sum[i]-sum[k])^2);}\)

#include <cmath>
#include <cstdio>
#include <vector>
#include <climits>
#include <cstring>
#include <algorithm>
using namespace std;
#define isdigit(x) ('0' <= (x)&&(x) <= '9')
template<typename T>
inline T Read(T Type)
{
    T x = 0;
    char a;
    while(!isdigit(a)) a = getchar();
    while(isdigit(a)) x = (x << 3) + (x << 1) + a - '0',a = getchar();
    return x;
}
const int MAXN = 3005;
const int inf = INT_MAX;
int x[MAXN],sum[MAXN],f[MAXN][MAXN];
inline int dmult(int x) {return x * x;}
int main()
{
    int i,j,l,n = Read(1),m = Read(1);
    memset(f,0x3f,sizeof(f));
    for(i = 1;i <= n;i++)
    {
        x[i] = Read(1);
        sum[i] += sum[i - 1] + x[i];
    }
    f[0][0] = 0;
    for(i = 1;i <= n;i++)
    {
        for(l = 1;l <= min(i,m);l++)
        {
            for(j = 0;j < i;j++)
                f[i][l] = min(f[i][l],f[j][l - 1] + dmult(sum[i] - sum[j]));
        }
    }
    printf("%d",m * f[n][m] - dmult(sum[n]));
    return 0;
}

算一下时间复杂度

\(3000^3 >10^9\)

显然不行

\(\color{pink}{TLE}\)

明显得优化下

看下标签 嗯 斜率优化

\(j>k\)

当且仅当

\(\color{pink}{f[j][l - 1] + dmult(sum[i] - sum[j]) < f[k][l - 1] + dmult(sum[i] - sum[k])}\)

我们认为j比k优

否则 k更优

化简一下得到

\(\color{pink}{f[j][l-1]+sum[i]^2+sum[j]^2-2*sum[i]*sum[j]<f[k][l - 1] + sum[i]^2 + sum[k]^2-2*sum[i]*sum[k])}\)

\(\color{pink}{f[j][l-1]-f[k][l-1]+sum[j]^2-sum[k]^2<2*sum[i]*(sum[j]-sum[k])}\)

因为我们设了 \(j>k\)

所以\(sum[j]-sum[k]>0\)

所以

\(\color{pink}{\frac{f[j][l-1]-f[k][l-1]+sum[j]^2-sum[k]^2}{(sum[j]-sum[k])}<2*sum[i]}\)

\(\color{pink}{\frac{f[j][l-1]+sum[j]^2-f[k][l-1]-sum[k]^2}{(sum[j]-sum[k])}<2*sum[i]}\)

非常明显的斜率优化

P.S最后输出的时候按照我推出来的也行

#include <cmath>
#include <cstdio>
#include <vector>
#include <climits>
#include <cstring>
#include <algorithm>
using namespace std;
#define isdigit(x) ('0' <= (x)&&(x) <= '9')
template<typename T>
inline T Read(T Type)
{
    T x = 0;
    char a;
    while(!isdigit(a)) a = getchar();
    while(isdigit(a)) x = (x << 3) + (x << 1) + a - '0',a = getchar();
    return x;
}
const int MAXN = 3005;
const int inf = INT_MAX;
int x[MAXN],sum[MAXN],f[MAXN][MAXN],q[MAXN],g[MAXN];
inline int dmult(int x) {return x * x;}
inline double count_k(int u,int l,int r)
{
    return (f[l][u] - f[r][u] + g[l] - g[r]) / (double)(sum[l] - sum[r]);
}
int main()
{
    int i,j,l,n = Read(1),m = Read(1);
    for(i = 1;i <= n;i++)
    {
        x[i] = Read(1);
        sum[i] += sum[i - 1] + x[i];
        g[i] = dmult(sum[i]);
    }
    int left,r;
    for(i = 1;i <= n;i++) f[i][1] = dmult(sum[i]);
    for(l = 2;l <= m;l++)
    {
        left = 1,r = 0;
        for(i = 1;i <= n;i++)
        {
            while(left < r&&count_k(l - 1,q[left],q[left + 1]) < 2 * sum[i]) left++;
            f[i][l] = f[q[left]][l - 1] + dmult(sum[i] - sum[q[left]]);
            while(left < r&&count_k(l - 1,q[r - 1],q[r]) > count_k(l - 1,q[r],i)) r--;
            q[++r] = i;
        }
    }
    printf("%d",m * f[n][m] - dmult(sum[n]));
   //按我推出的式子也行
    return 0;
}

转载于:https://www.cnblogs.com/resftlmuttmotw/p/11334847.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值