bzoj4518【SDOI2016】征途

4518: [Sdoi2016]征途

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 452   Solved: 295
[ Submit][ Status][ Discuss]

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

Source

鸣谢Menci上传




斜率优化DP(有公式恐惧症的我又懒得写过程了...




#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<map>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 3005
#define inf 1000000000000000000ll
using namespace std;
int n,m,head,tail,q[maxn];
ll ave,a[maxn],sum[maxn],f[maxn],g[maxn];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline double getk(int x,int y)
{
	return (double)(g[x]-g[y])/(double)(sum[x]-sum[y]);
}
int main()
{
	n=read();m=read();
	F(i,1,n)
	{
		a[i]=read();
		ave+=a[i];a[i]*=m;sum[i]=sum[i-1]+a[i];
	}
	F(i,1,n) f[i]=(sum[i]-ave)*(sum[i]-ave);
	F(j,2,m)
	{
		F(i,1,n) g[i]=f[i]+sum[i]*sum[i];
		head=tail=1;q[1]=j-1;
		F(i,j,n)
		{
			ll tmp=(sum[i]-ave)*2;
			while (head<tail&&getk(q[tail-1],q[tail])>getk(q[tail],i)) tail--;
			q[++tail]=i;
			while (head<tail&&getk(q[head],q[head+1])<=tmp) head++;
			f[i]=sum[i]*sum[i]+ave*ave-2*sum[i]*ave+g[q[head]]-tmp*sum[q[head]];
		}
	}
	printf("%lld\n",f[n]/m);
	return 0;
}


你可能感兴趣的:(bzoj,斜率优化DP)