P4072 [SDOI2016]征途 斜率优化
题意:
分析:
- 暴力
$ O(n^3) $ 枚举转移,期望得分 \(60pts\)
- 正解
化简式子,由于 m 和 n 是定值,所以平均数也可以看做定值
\(\large s^2 = \frac{\sum(a_i-\frac{sum}{m})^2}{m}=\frac{\sum {a_i}^2-2\times\frac{sum}{m}\sum a_i+\sum {\frac{sum}{m}}^2}{m}\)
\(\large s^2 =\frac{\sum {a_i}^2-2\times{\frac{sum}{m}}\times sum+m\times \frac{sum^2}{m^2}}{m}\)
\(\large s^2 = \frac{\sum {a_i}^2}{m}-\frac{sum^2}{m^2}\)
将 \(m^2\) 带入
\(ans= m\times s^2 = m\times\sum {a_i}^2-sum^2\)
问题转化成了求 \(\sum {a_i}^2\) 的最小值,我们发现这个形式很 斜率优化
设 \(j>k\) 且 \(j\) 转移比 \(k\) 更优,那么有
\(f_j+(sum_i-sum_j)^2<f_k+(sum_i-sum_k)^2\)
\(f_j+{sum_j}^2-2\times sum_i\times sum_j<f_k+{sum_k}^2-2\times sum_i\times sum_k\)
\(\large \frac{(f_j+{sum_j}^2)+({sum_k}^2+f_k)}{sum_j-sum_k}< 2\times sum_i\)
维护 \(f_j+{sum_j}^2\) 就可以了
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 3005;
int n,m,h,t;
int f[maxn][maxn],s1[maxn],s2[maxn],a[maxn],q[maxn];
int sqr(int x){return x*x;}
int upn(int id,int j,int k){return f[id][j]+s2[j]-f[id][k]-s2[k];}
int dwn(int j,int k){return s1[j]-s1[k];}
void work()
{
memset(f,0x3f,sizeof(f));
n=read();m=read();
for(int i=1;i<=n;i++) a[i]=read(),s1[i]=s1[i-1]+a[i],s2[i]=sqr(s1[i]);
for(int i=1;i<=n;i++) f[1][i]=s2[i];
for(int j=2;j<=m;j++)
{
h=1,t=0;
for(int i=1;i<=n;i++)
{
while(h<t&&upn(j-1,q[h+1],q[h])<2*s1[i]*dwn(q[h+1],q[h])) h++;
f[j][i]=f[j-1][q[h]]+sqr(s1[i]-s1[q[h]]);
while(h<t&&upn(j-1,q[t],q[t-1])*dwn(i,q[t])>upn(j-1,i,q[t])*dwn(q[t],q[t-1])) t--;
q[++t]=i;
}
}
printf("%lld\n",1ll*m*f[m][n]-sqr(s1[n]));
}
}
int main()
{
zzc::work();
return 0;
}