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
1 2 5 8 6
Sample Output
36
HINT
1≤n≤3000,保证从 S 到 T 的总路程不超过 30000
m天同序列分割做法。式子贼好推。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxn=3010; long long n,m,c[maxn],f[maxn],g[maxn],zz[maxn]; long long aa;char cc; long long read() { aa=0;cc=getchar(); while(cc<'0'||cc>'9') cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); return aa; } bool ok(int x,int y,int z) { return (c[y-1]*c[y-1]+g[y-1]-c[x-1]*c[x-1]-g[x-1])*(c[z-1]-c[y-1])>(c[z-1]*c[z-1]+g[z-1]-c[y-1]*c[y-1]-g[y-1])*(c[y-1]-c[x-1]); } void dp(long long x){ int l=1,r=0; for(int i=x;i<=n;++i) { while(l<r&&ok(zz[r-1],zz[r],i)) r--; zz[++r]=i; while(l<r&&c[zz[l]-1]*c[zz[l]-1]+g[zz[l]-1]-2*c[i]*c[zz[l]-1]>c[zz[l+1]-1]*c[zz[l+1]-1]+g[zz[l+1]-1]-2*c[i]*c[zz[l+1]-1]) l++; f[i]=c[zz[l]-1]*c[zz[l]-1]+g[zz[l]-1]-2*c[i]*c[zz[l]-1]+c[i]*c[i]; } memcpy(g,f,sizeof(f)); } int main() { n=read();m=read(); for(int i=1;i<=n;++i) c[i]=read(),c[i]+=c[i-1]; for(int i=1;i<=n;++i) g[i]=c[i]*c[i]; for(int i=2;i<=m;++i) dp(i); printf("%lld",g[n]*m-c[n]*c[n]); return 0; }