有 \(n\) 段路,需要分成 \(m\) 天走,每天走的必须是完整的若干段。希望每天走的路程的方差尽量小,求最小方差 \(\times m^2\)。\(n \leq 3000\)
设 \(f[i]\) 表示到某天走了 \(i\) 段,\(g[i]\) 表示到上一天走了 \(i\) 段,对 \(g[i]\) 建凸包,单调队列维护,转移到 \(f[i]\),转移方程为
\[
f[i]=\min_{j<i} g[j]+s^2[i]-2s[i]s[j]+s^2[j]
\]
化为斜率优化形式为
\[
g[j]+s^2[j]=2s[i]\cdot s[j]-s^2[i]+f[i]
\]
即 \(y[j]=g[j]+s^2[j], x[j]=s[j], A=2s[i], B=-s^2[i]\)
1A了,很舒服
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3005;
int n,m,s[N],f[N],g[N],q[N],head,tail,sum;
double slope(int i,int j) {
return 1.0*(g[i]+s[i]*s[i]-g[j]-s[j]*s[j])/(s[i]-s[j]);
}
signed main() {
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>s[i], sum+=s[i];
for(int i=1;i<=n;i++) s[i]+=s[i-1];
for(int i=1;i<=n;i++) f[i]=s[i]*s[i];
for(int t=2;t<=m;t++) {
memcpy(g,f,sizeof f);
head=1; tail=0;
for(int i=1;i<=n;i++) {
while(head<tail && slope(q[head+1],q[head])<2*s[i]) ++head;
int j=q[head];
f[i]=g[j]+s[i]*s[i]-2*s[i]*s[j]+s[j]*s[j];
while(head<tail && slope(q[tail-1],q[tail])>slope(q[tail],i)) --tail;
q[++tail]=i;
}
}
cout<<m*f[n]-sum*sum;
}
原文:https://www.cnblogs.com/mollnn/p/12464486.html