bzoj4518 征途 斜率优化

       令f[i][j]表示走i段当前在j点的最优答案。首先所有数都*m,最后再/m,令ave表示平均数,显然有f[i][j]=min{f[i-1][k]+(s[i]-s[k]-ave)^2}

       很显然的斜率优化。对于i,由j>k,f[i-1][j]+(s[i]-s[j]-ave)^2<=f[i-1][k]+(s[i]-s[k]-ave)^2,化简得到(h[j]-h[k])/2/(s[j]-s[k])<=s[i],其中h[j]=f[i-1][j]+s[j]^2+2*ave*s[j],然后单调队列维护一下即可。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define sqr(x) (ll)(x)*(x)
#define inf 100000000000000000LL
#define N 3005
using namespace std;

int n,m,t,a[N],s[N],q[N]; ll f[2][N],h[N];
double getk(int x,int y){
	return (double)(h[y]-h[x])/(s[y]-s[x])/2;
}
int main(){
	scanf("%d%d",&n,&m); int i,j,head,tail;
	for (i=1; i<=n; i++){
		scanf("%d",&a[i]); t+=a[i];
		a[i]*=m; s[i]=s[i-1]+a[i];
	}
	for (i=1; i<=n; i++) f[0][i]=sqr(s[i]-t); f[0][0]=inf;
	int now=0,last;
	for (i=2; i<=m; i++){
		last=now; now^=1;
		for (j=0; j<=n; j++) f[now][j]=inf;
		head=tail=1; q[1]=0; h[0]=f[last][0];
		for (j=1; j<=n; j++){
			while (head<tail && getk(q[head],q[head+1])<=s[j]) head++;
			f[now][j]=f[last][q[head]]+sqr(s[j]-s[q[head]]-t);
			h[j]=f[last][j]+sqr(s[j])+(ll)s[j]*t*2;
			while (head<tail && getk(q[tail],j)<getk(q[tail-1],q[tail])) tail--;
			q[++tail]=j;
		}
	}
	printf("%lld\n",f[now][n]/m);
	return 0;
}


by lych

2016.4.23

你可能感兴趣的:(bzoj4518 征途 斜率优化)