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

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]);
}

 

posted @ 2018-05-10 22:01  fcwww  阅读(179)  评论(0编辑  收藏  举报