bzoj4518【SDOI2016】征途

来源:互联网 发布:手机cmd软件 编辑:程序博客网 时间:2024/04/30 14:10

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 1000000000000000000llusing 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;}


0 0