Description
Pine开始了从S地到T地的征途。
从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。
Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。
Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。
帮助Pine求出最小方差是多少。
设方差是v,可以证明,v×m^2是一个整数。为了避免精度误差,输出结果时输出v×m^2。
Input
第一行两个数 n、m。
第二行 n 个数,表示 n 段路的长度
Output
一个数,最小方差乘以 m^2 后的值
Sample Input
5 2
1 2 5 8 6
Sample Output
36
HINT
1≤n≤3000,保证从 S 到 T 的总路程不超过 30000
Source
鸣谢Menci上传
我们可以发现平均数是不变的,所以最终答案(设ans=m*答案)我们可以先简化一下.
令第i天走的路的长度为x[i],平均数为s,所以x[i]的和为sum=s*m。
ans=m2∗∑i=1m(x[i]−s)2
=m2∗∑i=1m(x[i]2−2∗x[i]∗s+s2)
=m2∗(∑i=1mx[i]2−2∗∑i=1mx[i]∗s+s2∗m)
=m2∗(∑i=1mx[i]2−2∗s2∗m+s2∗m)
又因为s=summ,所以ans=m2∗∑i=1mx[i]2−2∗m∗sum2+∗m∗sum2=m2∗∑i=1mx[i]2−m∗sum2
所以最终答案是要除以m的,即最终答案为
m∗∑i=1mx[i]2−sum2
所以我们只要用DP来维护x[i]^2即可,然后我们可以用斜率优化就能通过了!
Tip: 在计算斜率时要强制转换成double!具体请看代码。
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,now,q[3020];
long long sum[3020],a[3020],f[3020][3];
double g(int j,int k)
{
return (double)(f[k][now^1]-f[j][now^1]+sum[k]*sum[k]-sum[j]*sum[j])/(double)(2*(sum[k]-sum[j]));
}
int main()
{
scanf("%d%d",&n,&m);
sum[0]=0;
for (int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
now=0;
for (int i=1;i<=n;i++) f[i][now]=sum[i]*sum[i];
for (int l=2;l<=m;l++)
{
now=now^1;
int head=1,tail=1;q[1]=0;
f[0][now]=0;
for (int i=1;i<=n;i++)
{
while (head<tail&&g(q[head],q[head+1])<=sum[i]) head++;
int j=q[head];
f[i][now]=f[j][now^1]+(sum[i]-sum[j])*(sum[i]-sum[j]);
while (head<tail&&g(q[tail-1],q[tail])>=g(q[tail],i)) tail--;
q[++tail]=i;
}
}
printf("%lld\n",f[n][now]*m-sum[n]*sum[n]);
return 0;
}