题意
Pine开始了从S地到T地的征途。
从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。
Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。
Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。
帮助Pine求出最小方差是多少。
设方差是v,可以证明,v×m2v×m^2v×m2是一个整数。为了避免精度误差,输出结果时输出v×m2v×m^2v×m2。
题解
此题有斜率优化做法
但是这篇blog是想介绍带权二分
首先推推式子
设xi为第i天走的步数,
那么显然有∑i=1mxi=mx‾=S\sum_{i=1}^mx_i=m\overline x=S∑i=1mxi=mx=S
v=∑i=1m(xi−x‾)2m=∑i=1m(xi2−2x‾xi+x‾2)m=∑i=1mxi2−∑i=1m2x‾xi+∑i=1mx‾2m=∑i=1mxi2−mx‾2mv=\frac{\sum_{i=1}^m(x_i-\overline{x})^2}{m}=\frac{\sum_{i=1}^m(x_i^2-2\overline{x}x_i+\overline{x}^2)}{m}=\frac{\sum_{i=1}^mx_i^2-\sum_{i=1}^m2\overline{x}x_i+\sum_{i=1}^m\overline{x}^2}{m}=\frac{\sum_{i=1}^mx_i^2-m\overline x^2}{m}v=m∑i=1m(xi−x)2=m∑i=1m(xi2−2xxi+x2)=m∑i=1mxi2−∑i=1m2xxi+∑i=1mx2=m∑i=1mxi2−mx2
然后v⋅m2=m∑i=1mxi2−(mx‾)2=m∑i=1mxi2−S2v\cdot m^2=m\sum_{i=1}^mx_i^2-(m\overline x)^2=m\sum_{i=1}^mx_i^2-S^2v⋅m2=mi=1∑mxi2−(mx)2=mi=1∑mxi2−S2
这个式子只与∑i=1mxi2\sum_{i=1}^mx_i^2∑i=1mxi2有关
那么我们就把问题转换成了:有n个数,你要把他们分成连续的m段,求最小的平方和
那么可有dp(i,j)表示前i个数,恰好分成j段
dp(i,j)=min(dp(k,j−1)+(S[i]−S[k])2)dp(i,j)=min(dp(k,j-1)+(S[i]-S[k])^2)dp(i,j)=min(dp(k,j−1)+(S[i]−S[k])2)
显然会T
于是对于这种限制恰好个数的,我们就可以上今天的主角带权二分了
首先显然可以发现的一点是随着j的增大,dp(n,j)是一个下凸的曲线 (怎么证?咕咕咕…)
感性的想(理性的请转步论文),我们可以给每次增加段数的时候加上一个额外代价(或者额外收益),进而控制达到最大值时的段数
然后我们可以通过二分来调控这个额外值
最后再减去即可
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=3005;
const int INF=0x3f3f3f3f;
int x[N];
int s[N];
int n,m;
int X;
ll d[N];
int f[N];
bool check(){
memset(d,0x3f,sizeof d);
d[0]=f[0]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++){
if(d[i]>d[j]+1ll*(s[i]-s[j])*(s[i]-s[j])+X){
d[i]=d[j]+1ll*(s[i]-s[j])*(s[i]-s[j])+X;
f[i]=f[j]+1;
}
else if(d[i]==d[j]+1ll*(s[i]-s[j])*(s[i]-s[j])+X&&f[i]<f[j]+1){
f[i]=f[j]+1;
}
}
return f[n]>=m;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&x[i]);
for(int i=1;i<=n;i++)
s[i]=s[i-1]+x[i];
int l=-INF,r=INF;
int ansX;
while(l<=r){
X=(l+r)>>1;
if(check()){
ansX=X;
l=X+1;
}
else{
r=X-1;
}
}
X=ansX;
check();
ll ans=(d[n]-m*X)*m-1ll*s[n]*s[n];
printf("%lld\n",ans);
}