4518: [Sdoi2016]征途
Time Limit: 10 Sec Memory Limit: 256 MBDescription
Input
Output
一个数,最小方差乘以 m^2 后的值
Sample Input
1 2 5 8 6
Sample Output
HINT
1≤n≤3000,保证从 S 到 T 的总路程不超过 30000
x[i]表示第i天走的距离,X表示x[i]的平均值=(x[1]+x[2]+……x[m])/m
m^2 中把1个m 和 算方差的 除m 抵消
ans=min 【(X-x[1])^2 + (X-x[2])^2 + ……(X- x[m])^2 】*m
= m*【m*X^2 + (x[1]^2+x[2]^2+……x[m]^2) - 2*X*(x[1]+x[2]+……x[m])】
= m*(x[1]^2+x[2]^2+……x[m]^2)- (x[1]+x[2]+……x[m])^2
只需要DP维护 (x[1]^2+x[2]^2+……x[m]^2)的最小值即可
斜率优化
#include<cstdio> #include<algorithm> #define N 3001 using namespace std; int a[N]; long long sum[N],now[N],last[N]; int q[N],head,tail; long long up(int k,int j) { return last[j]-last[k]+sum[j]*sum[j]-sum[k]*sum[k]; } long long down(int k,int j) { return 2*(sum[j]-sum[k]); } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } for(int i=1;i<=n;i++) last[i]=sum[i]*sum[i]; for(int k=2;k<=m;k++) { head=tail=1; q[1]=k-1; for(int i=k;i<=n;i++) { while(head<tail && up(q[head],q[head+1])<=down(q[head],q[head+1])*sum[i]) head++; now[i]=last[q[head]]+(sum[i]-sum[q[head]])*(sum[i]-sum[q[head]]); while(head<tail && up(q[tail-1],q[tail])*down(q[tail],i)>=up(q[tail],i)*down(q[tail-1],q[tail])) tail--; q[++tail]=i; } swap(now,last); } printf("%lld",m*last[n]-sum[n]*sum[n]); }