BZOJ_4518_[Sdoi2016]征途_斜率优化
BZOJ_4518_[Sdoi2016]征途_斜率优化
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
1 2 5 8 6
Sample Output
36
HINT
1≤n≤3000,保证从 S 到 T 的总路程不超过 30000
$\sum (x_i-\bar{x})^{2}*m$
$=(\sum x_i^2-2\sum x_i\bar{x}+\frac{sum^2}{m})*m$
$=m\sum x_i^2-sum^2$
于是转化为分成m段,求每段和的平方和的最小值。
设F[i][j]表示前i个数分成j段的最小答案 有F[i][j]=min(F[i][j],F[i-1][k]+(s[k]-s[j])*(s[k]-s[j]))
设两个决策点k,l,l>k且l比k优。
G[j]=2*s[j]>(f[i][k]-f[i][l]+s[k]*s[k]-s[l]*s[l])/(s[k]-s[l]);
用单调队列维护一个上凸包。
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; typedef double f2; typedef long long ll; #define N 3050 int a[N],n,m,Q[N],L,R; ll f[N][N],s[N]; f2 slope(int i,int k,int l) { return (1.0*f[i][k]-f[i][l]+s[k]*s[k]-s[l]*s[l])/(s[k]-s[l]); } int main() { scanf("%d%d",&n,&m); int i,j; for(i=1;i<=n;i++) { scanf("%d",&a[i]); s[i]=s[i-1]+a[i]; f[1][i]=s[i]*s[i]; } f[0][0]=0; for(i=2;i<=m;i++) { L=R=0; for(j=1;j<=n;j++) { while(L<R-1&&slope(i-1,Q[L],Q[L+1])<2*s[j]) L++; int k=Q[L]; f[i][j]=f[i-1][k]+(s[j]-s[k])*(s[j]-s[k]); while(L<R-1&&slope(i-1,Q[R-1],j)<slope(i-1,Q[R-1],Q[R-2])) R--; Q[R++]=j; } } printf("%lld\n",m*f[m][n]-s[n]*s[n]); }