【bzoj4518】 Sdoi2016—征途

http://www.lydsy.com/JudgeOnline/problem.php?id=4518 (题目链接)

题意

  给出n个连续的整数,求将它们分成m段,求最小方差*m^2。

Solution

  把m^2乘进去,然后就约掉了一大堆东西,我们用${f_{i,j}}$表示前j个数分成i组的最小值:

$${f_{i,j}=Min\{f_{i-1,k}+(S_i-S_k)^2\}}$$

  显然这可以斜率优化。然后就斜率优化咯。

细节

  最近很不爽,头晕炸了。。一道sb题写了2个小时。。。我真是zz

代码

// bzoj4518
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=3010;
int q[maxn],n,m,p;
LL s[maxn],f[2][maxn];

double slope(int i,int j) {
	return (double)(f[p^1][i]+m*s[i]*s[i]-f[p^1][j]-m*s[j]*s[j])/(s[i]-s[j]);
}
int main() {
	scanf("%d%d",&n,&m);
	for (int x,i=1;i<=n;i++) scanf("%d",&x),s[i]=s[i-1]+x;
	for (int i=1;i<=n;i++) f[0][i]=m*s[i]*s[i];
	for (int j=2;j<=m;j++) {
		int l=1,r=0;p^=1;
		for (int i=j-1;i<j;i++) q[++r]=i;
		for (int i=j;i<=n;i++) {
			while (l<r && slope(q[l],q[l+1])<=2*m*s[i]) l++;
			f[p][i]=f[p^1][q[l]]+m*(s[i]-s[q[l]])*(s[i]-s[q[l]]);
			while (l<r && slope(q[r-1],q[r])>slope(q[r],i)) r--;
			q[++r]=i;
		}
	}
	printf("%lld",f[p][n]-s[n]*s[n]);
	return 0;
}

  

posted @ 2016-12-31 21:48  MashiroSky  阅读(277)  评论(0编辑  收藏  举报