[SDOI2016]征途
Posted resftlmuttmotw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2016]征途相关的知识,希望对你有一定的参考价值。
Solution
方差
=\(\frac\displaystyle\sum^m_i=1(x_i-\barx)^2m\)
=\(\displaystyle\sum^m_i=1(x_i^2 + \barx^2 - 2*x_i*\barx)\)
=\(\displaystyle\sum^m_i=1(x_i^2+\frac\displaystyle\sum^m_i=1x_im- 2*x_i*\frac\displaystyle\sum^m_i=1x_im)\)
=\(\displaystyle\sum^m_i=1x_i^2+\displaystyle\sum^m_i=1x_i-\frac\displaystyle\sum^m_i=1(2*x_i*\displaystyle\sum^m_i=1x_i)m\)
=\(\displaystyle\sum^m_i=1x_i^2+\displaystyle\sum^m_i=1x_i-\frac\displaystyle\sum^m_i=1x_i*\displaystyle\sum^m_i=1(2*x_i)m\)
=\(\displaystyle\sum^m_i=1x_i^2+\displaystyle\sum^m_i=1x_i-\frac2*(\displaystyle\sum^m_i=1x_i)^2m\)
易得\(\displaystyle\sum^m_i=1x_i\)和\(\displaystyle\sum^m_i=1x_i\)是定值
所以只需要求\(\displaystyle\sum^m_i=1x_i^2\)的最小值就行了
然后再套上公式 注意得*上\(m^2\)
然后非常容易想到DP
定义
dp[i][j]表示前i个分成j份的平方和的最小值
易得状态转移方程
\(\colorpinkdp[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\)
显然不行
\(\colorpinkTLE\)
明显得优化下
看下标签 嗯 斜率优化
设\(j>k\)
当且仅当
\(\colorpinkf[j][l - 1] + dmult(sum[i] - sum[j]) < f[k][l - 1] + dmult(sum[i] - sum[k])\)
我们认为j比k优
否则 k更优
化简一下得到
\(\colorpinkf[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])\)
\(\colorpinkf[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\)
所以
\(\colorpink\fracf[j][l-1]-f[k][l-1]+sum[j]^2-sum[k]^2(sum[j]-sum[k])<2*sum[i]\)
\(\colorpink\fracf[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;
以上是关于[SDOI2016]征途的主要内容,如果未能解决你的问题,请参考以下文章